diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000000..337b0b15e740 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,57 @@ +--- + +codecov: + notify: + after_n_builds: 9 # Number of test matrix+lint jobs uploading coverage + wait_for_ci: false + + require_ci_to_pass: false + + token: >- # repo-scoped, upload-only, needed for stability in PRs from forks + 2b8c7a7a-7293-4a00-bf02-19bd55a1389b + +comment: + require_changes: true + +coverage: + range: 100..100 + status: + patch: + default: + target: 100% + pytest: + target: 100% + flags: + - pytest + typing: + flags: + - MyPy + project: + default: + target: 75% + lib: + flags: + - pytest + paths: + - awx/ + target: 75% + tests: + flags: + - pytest + paths: + - tests/ + - >- + **/test/ + - >- + **/tests/ + - >- + **/test/** + - >- + **/tests/** + target: 95% + typing: + flags: + - MyPy + target: 100% + +... diff --git a/.coveragerc b/.coveragerc index f9ef3447bfec..14aa98d8f966 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,16 +1,6 @@ -[run] -source = awx -branch = True -omit = - awx/main/migrations/* - awx/lib/site-packages/* - [report] # Regexes for lines to exclude from consideration -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - +exclude_also = # Don't complain about missing debug-only code: def __repr__ if self\.debug @@ -23,7 +13,35 @@ exclude_lines = if 0: if __name__ == .__main__.: -ignore_errors = True + ^\s*@pytest\.mark\.xfail + +[run] +branch = True +# NOTE: `disable_warnings` is needed when `pytest-cov` runs in tandem +# NOTE: with `pytest-xdist`. These warnings are false negative in this +# NOTE: context. +# +# NOTE: It's `coveragepy` that emits the warnings and previously they +# NOTE: wouldn't get on the radar of `pytest`'s `filterwarnings` +# NOTE: mechanism. This changed, however, with `pytest >= 8.4`. And +# NOTE: since we set `filterwarnings = error`, those warnings are being +# NOTE: raised as exceptions, cascading into `pytest`'s internals and +# NOTE: causing tracebacks and crashes of the test sessions. +# +# Ref: +# * https://github.com/pytest-dev/pytest-cov/issues/693 +# * https://github.com/pytest-dev/pytest-cov/pull/695 +# * https://github.com/pytest-dev/pytest-cov/pull/696 +disable_warnings = + module-not-measured +omit = + awx/main/migrations/* + awx/settings/defaults.py + awx/settings/*_defaults.py +source = + . +source_pkgs = + awx [xml] output = ./reports/coverage.xml diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 0164155b8102..31368dcb706b 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,3 +1,3 @@ # Community Code of Conduct -Please see the official [Ansible Community Code of Conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html). +Please see the official [Ansible Community Code of Conduct](https://docs.ansible.com/projects/ansible/latest/community/code_of_conduct.html). diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9cca894822ff..144f4599eb84 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -13,12 +13,14 @@ body: attributes: label: Please confirm the following options: - - label: I agree to follow this project's [code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html). + - label: I agree to follow this project's [code of conduct](https://docs.ansible.com/projects/ansible/latest/community/code_of_conduct.html). required: true - label: I have checked the [current issues](https://github.com/ansible/awx/issues) for duplicates. required: true - label: I understand that AWX is open source software provided for free and that I might not receive a timely response. required: true + - label: I am **NOT** reporting a (potential) security vulnerability. (These should be emailed to `security@ansible.com` instead.) + required: true - type: textarea id: summary @@ -42,6 +44,7 @@ body: label: Select the relevant components options: - label: UI + - label: UI (tech preview) - label: API - label: Docs - label: Collection diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index b3e5d26591c8..88a8445f2789 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -5,7 +5,7 @@ contact_links: url: https://github.com/ansible/awx#get-involved about: For general debugging or technical support please see the Get Involved section of our readme. - name: 📝 Ansible Code of Conduct - url: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser + url: https://docs.ansible.com/projects/ansible/latest/community/code_of_conduct.html?utm_medium=github&utm_source=issue_template_chooser about: AWX uses the Ansible Code of Conduct; ❤ Be nice to other members of the community. ☮ Behave. - name: 💼 For Enterprise url: https://www.ansible.com/products/engine?utm_medium=github&utm_source=issue_template_chooser diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 53ba31b9f6d1..d1c81fef755f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -13,7 +13,7 @@ body: attributes: label: Please confirm the following options: - - label: I agree to follow this project's [code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html). + - label: I agree to follow this project's [code of conduct](https://docs.ansible.com/projects/ansible/latest/community/code_of_conduct.html). required: true - label: I have checked the [current issues](https://github.com/ansible/awx/issues) for duplicates. required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 07d23adf000b..99a30cb41125 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,8 @@ ##### ISSUE TYPE @@ -16,20 +17,14 @@ the change does. ##### COMPONENT NAME - API - - UI - Collection - CLI - Docs - Other -##### AWX VERSION - -``` - -``` -##### ADDITIONAL INFORMATION +##### STEPS TO REPRODUCE AND EXTRA INFO ' reports/coverage.xml + echo "Injected PR number ${{ github.event.pull_request.number }} into reports/coverage.xml" + fi + if [ -f "awxkit/coverage.xml" ]; then + sed -i '2i' awxkit/coverage.xml + echo "Injected PR number ${{ github.event.pull_request.number }} into awxkit/coverage.xml" + fi + + - name: Upload test coverage to Codecov + if: >- + !cancelled() + && steps.make-run.outputs.cov-report-files != '' + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: >- + ${{ + toJSON(env.UPSTREAM_REPOSITORY_ID == github.repository_id) + }} + files: >- + ${{ steps.make-run.outputs.cov-report-files }} + flags: >- + CI-GHA, + pytest, + OS-${{ + runner.os + }} + token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload test results to Codecov + if: >- + !cancelled() + && steps.make-run.outputs.test-result-files != '' + uses: codecov/test-results-action@v1 + with: + fail_ci_if_error: >- + ${{ + toJSON(env.UPSTREAM_REPOSITORY_ID == github.repository_id) + }} + files: >- + ${{ steps.make-run.outputs.test-result-files }} + flags: >- + CI-GHA, + pytest, + OS-${{ + runner.os + }} + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.tests.name }}-artifacts + path: | + reports/coverage.xml + awxkit/coverage.xml + retention-days: 5 + + - name: >- + Upload ${{ + matrix.tests.coverage-upload-name || 'awx' + }} jUnit test reports to the unified dashboard + if: >- + !cancelled() + && steps.make-run.outputs.test-result-files != '' + && github.event_name == 'push' + && env.UPSTREAM_REPOSITORY_ID == github.repository_id + && github.ref_name == github.event.repository.default_branch + uses: ansible/gh-action-record-test-results@3784db66a1b7fb3809999a7251c8a7203a7ffbe8 + with: + aggregation-server-url: ${{ vars.PDE_ORG_RESULTS_AGGREGATOR_UPLOAD_URL }} + http-auth-password: >- + ${{ secrets.PDE_ORG_RESULTS_UPLOAD_PASSWORD }} + http-auth-username: >- + ${{ vars.PDE_ORG_RESULTS_AGGREGATOR_UPLOAD_USER }} + project-component-name: >- + ${{ matrix.tests.coverage-upload-name || 'awx' }} + test-result-files: >- + ${{ steps.make-run.outputs.test-result-files }} dev-env: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + show-progress: false - - name: Run smoke test - run: make github_ci_setup && ansible-playbook tools/docker-compose/ansible/smoke-test.yml -v + - uses: ./.github/actions/setup-python + with: + python-version: '3.13' + + - uses: ./.github/actions/run_awx_devel + id: awx + with: + build-ui: false + github-token: ${{ secrets.GITHUB_TOKEN }} + private-github-key: ${{ secrets.PRIVATE_GITHUB_KEY }} + + - name: Run live dev env tests + run: docker exec tools_awx_1 /bin/bash -c "make live_test" + + - uses: ./.github/actions/upload_awx_devel_logs + if: always() + with: + log-filename: live-tests.log awx-operator: runs-on: ubuntu-latest + timeout-minutes: 60 + env: + DEBUG_OUTPUT_DIR: /tmp/awx_operator_molecule_test steps: - name: Checkout awx - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: + show-progress: false path: awx + - uses: ./awx/.github/actions/setup-ssh-agent + with: + ssh-private-key: ${{ secrets.PRIVATE_GITHUB_KEY }} + - name: Checkout awx-operator - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: + show-progress: false\ repository: ansible/awx-operator path: awx-operator - - name: Get python version from Makefile - working-directory: awx - run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV - - - name: Install python ${{ env.py_version }} - uses: actions/setup-python@v2 + - name: Setup python, referencing action at awx relative path + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 with: - python-version: ${{ env.py_version }} + python-version: '3.12' - name: Install playbook dependencies run: | - python3 -m pip install docker + python -m pip install docker + - name: Check Python version + working-directory: awx + run: | + make print-PYTHON + - name: Build AWX image working-directory: awx run: | - ansible-playbook -v tools/ansible/build.yml \ - -e headless=yes \ - -e awx_image=awx \ - -e awx_image_tag=ci \ - -e ansible_python_interpreter=$(which python3) + VERSION=`make version-for-buildyml` make awx-kube-build + env: + COMPOSE_TAG: ci + DEV_DOCKER_TAG_BASE: local + HEADLESS: yes - name: Run test deployment with awx-operator working-directory: awx-operator + id: awx_operator_test + timeout-minutes: 60 + continue-on-error: true run: | - python3 -m pip install -r molecule/requirements.txt - ansible-galaxy collection install -r molecule/requirements.yml - sudo rm -f $(which kustomize) - make kustomize - KUSTOMIZE_PATH=$(readlink -f bin/kustomize) molecule -v test -s kind + set +e + timeout 15m bash -elc ' + python -m pip install -r molecule/requirements.txt + python -m pip install PyYAML # for awx/tools/scripts/rewrite-awx-operator-requirements.py + $(realpath ../awx/tools/scripts/rewrite-awx-operator-requirements.py) molecule/requirements.yml $(realpath ../awx) + ansible-galaxy collection install -r molecule/requirements.yml + sudo rm -f $(which kustomize) + make kustomize + KUSTOMIZE_PATH=$(readlink -f bin/kustomize) molecule -v test -s kind -- --skip-tags=replicas + ' + rc=$? + if [ $rc -eq 124 ]; then + echo "timed_out=true" >> "$GITHUB_OUTPUT" + fi + exit $rc env: - AWX_TEST_IMAGE: awx + AWX_TEST_IMAGE: local/awx AWX_TEST_VERSION: ci + AWX_EE_TEST_IMAGE: quay.io/ansible/awx-ee:latest + STORE_DEBUG_OUTPUT: true + + - name: Collect awx-operator logs on timeout + # Only run on timeout; normal failures should use molecule's built-in log collection. + if: steps.awx_operator_test.outputs.timed_out == 'true' + run: | + mkdir -p "$DEBUG_OUTPUT_DIR" + if command -v kind >/dev/null 2>&1; then + for cluster in $(kind get clusters 2>/dev/null); do + kind export logs "$DEBUG_OUTPUT_DIR/$cluster" --name "$cluster" || true + done + fi + if command -v kubectl >/dev/null 2>&1; then + kubectl get all -A -o wide > "$DEBUG_OUTPUT_DIR/kubectl-get-all.txt" || true + kubectl get pods -A -o wide > "$DEBUG_OUTPUT_DIR/kubectl-get-pods.txt" || true + kubectl describe pods -A > "$DEBUG_OUTPUT_DIR/kubectl-describe-pods.txt" || true + fi + docker ps -a > "$DEBUG_OUTPUT_DIR/docker-ps.txt" || true + + - name: Upload debug output + if: always() + uses: actions/upload-artifact@v4 + with: + name: awx-operator-debug-output + path: ${{ env.DEBUG_OUTPUT_DIR }} + + - name: Fail awx-operator check if test deployment failed + if: steps.awx_operator_test.outcome != 'success' + run: exit 1 collection-sanity: name: awx_collection sanity runs-on: ubuntu-latest + timeout-minutes: 30 strategy: fail-fast: false + matrix: + ansible: + - stable-2.17 + # - devel steps: - - uses: actions/checkout@v2 + - name: Perform sanity testing + uses: ansible-community/ansible-test-gh-action@release/v1 + with: + ansible-core-version: ${{ matrix.ansible }} + codecov-token: ${{ secrets.CODECOV_TOKEN }} + collection-root: awx_collection + pre-test-cmd: >- + ansible-playbook + -i localhost, + tools/template_galaxy.yml + -e collection_package=awx + -e collection_namespace=awx + -e collection_version=1.0.0 + -e '{"awx_template_version": false}' + testing-type: sanity - # The containers that GitHub Actions use have Ansible installed, so upgrade to make sure we have the latest version. - - name: Upgrade ansible-core - run: python3 -m pip install --upgrade ansible-core + - name: Upload awx jUnit test reports to the unified dashboard + if: >- + !cancelled() + && steps.make-run.outputs.test-result-files != '' + && github.event_name == 'push' + && env.UPSTREAM_REPOSITORY_ID == github.repository_id + && github.ref_name == github.event.repository.default_branch + uses: ansible/gh-action-record-test-results@3784db66a1b7fb3809999a7251c8a7203a7ffbe8 + with: + aggregation-server-url: ${{ vars.PDE_ORG_RESULTS_AGGREGATOR_UPLOAD_URL }} + http-auth-password: >- + ${{ secrets.PDE_ORG_RESULTS_UPLOAD_PASSWORD }} + http-auth-username: >- + ${{ vars.PDE_ORG_RESULTS_AGGREGATOR_UPLOAD_USER }} + project-component-name: awx + test-result-files: >- + ${{ steps.make-run.outputs.test-result-files }} - - name: Run sanity tests - run: make test_collection_sanity + collection-integration: + name: awx_collection integration + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + target-regex: + - name: a-h + regex: ^[a-h] + - name: i-p + regex: ^[i-p] + - name: r-z0-9 + regex: ^[r-z0-9] + steps: + - uses: actions/checkout@v4 + with: + show-progress: false + + - uses: ./.github/actions/setup-python + with: + python-version: '3.13' + + - name: Remove system ansible to avoid conflicts + run: | + python -m pip uninstall -y ansible ansible-core || true + + - uses: ./.github/actions/run_awx_devel + id: awx + with: + build-ui: false + github-token: ${{ secrets.GITHUB_TOKEN }} + private-github-key: ${{ secrets.PRIVATE_GITHUB_KEY }} + + - name: Install dependencies for running tests + run: | + python -m pip install -e ./awxkit/ + python -m pip install -r awx_collection/requirements.txt + hash -r # Rehash to pick up newly installed scripts + + - name: Run integration tests + id: make-run + run: | + echo "::remove-matcher owner=python::" # Disable annoying annotations from setup-python + echo '[general]' > ~/.tower_cli.cfg + echo 'host = https://${{ steps.awx.outputs.ip }}:8043' >> ~/.tower_cli.cfg + echo 'username = admin' >> ~/.tower_cli.cfg + echo 'password = password' >> ~/.tower_cli.cfg + echo 'verify_ssl = false' >> ~/.tower_cli.cfg + TARGETS="$(ls awx_collection/tests/integration/targets | grep '${{ matrix.target-regex.regex }}' | tr '\n' ' ')" + export PYTHONPATH="$(python -c 'import site; print(":".join(site.getsitepackages()))')${PYTHONPATH:+:$PYTHONPATH}" + make COLLECTION_VERSION=100.100.100-git COLLECTION_TEST_TARGET="--requirements $TARGETS" test_collection_integration env: - # needed due to cgroupsv2. This is fixed, but a stable release - # with the fix has not been made yet. ANSIBLE_TEST_PREFER_PODMAN: 1 + + - name: Upload test coverage to Codecov + if: >- + !cancelled() + && steps.make-run.outputs.cov-report-files != '' + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: >- + ${{ + toJSON(env.UPSTREAM_REPOSITORY_ID == github.repository_id) + }} + files: >- + ${{ steps.make-run.outputs.cov-report-files }} + flags: >- + CI-GHA, + ansible-test, + integration, + OS-${{ + runner.os + }} + token: ${{ secrets.CODECOV_TOKEN }} + + # Upload coverage report as artifact + - uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-${{ matrix.target-regex.name }} + path: ~/.ansible/collections/ansible_collections/awx/awx/tests/output/coverage/ + retention-days: 1 + + - uses: ./.github/actions/upload_awx_devel_logs + if: always() + with: + log-filename: collection-integration-${{ matrix.target-regex.name }}.log + + collection-integration-coverage-combine: + name: combine awx_collection integration coverage + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: + - collection-integration + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + show-progress: false + + - uses: ./.github/actions/setup-python + with: + python-version: '3.13' + + - name: Remove system ansible to avoid conflicts + run: | + python -m pip uninstall -y ansible ansible-core || true + + - name: Upgrade ansible-core + run: python -m pip install --upgrade ansible-core + + - name: Download coverage artifacts + uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: coverage + pattern: coverage-* + + - name: Combine coverage + run: | + make COLLECTION_VERSION=100.100.100-git install_collection + mkdir -p ~/.ansible/collections/ansible_collections/awx/awx/tests/output/coverage + cp -rv coverage/* ~/.ansible/collections/ansible_collections/awx/awx/tests/output/coverage/ + cd ~/.ansible/collections/ansible_collections/awx/awx + hash -r # Rehash to pick up newly installed scripts + PATH="$(python -c 'import sys; import os; print(os.path.dirname(sys.executable))'):$PATH" ansible-test coverage combine --requirements + PATH="$(python -c 'import sys; import os; print(os.path.dirname(sys.executable))'):$PATH" ansible-test coverage html + echo '## AWX Collection Integration Coverage' >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + PATH="$(python -c 'import sys; import os; print(os.path.dirname(sys.executable))'):$PATH" ansible-test coverage report >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo >> $GITHUB_STEP_SUMMARY + echo '## AWX Collection Integration Coverage HTML' >> $GITHUB_STEP_SUMMARY + echo 'Download the HTML artifacts to view the coverage report.' >> $GITHUB_STEP_SUMMARY + + - name: Upload coverage report as artifact + uses: actions/upload-artifact@v4 + with: + name: awx-collection-integration-coverage-html + path: ~/.ansible/collections/ansible_collections/awx/awx/tests/output/reports/coverage diff --git a/.github/workflows/dab-release.yml b/.github/workflows/dab-release.yml new file mode 100644 index 000000000000..6ebbe05bcecf --- /dev/null +++ b/.github/workflows/dab-release.yml @@ -0,0 +1,57 @@ +--- +name: django-ansible-base requirements update +on: + workflow_dispatch: + schedule: + - cron: '0 6 * * *' # once an day @ 6 AM +permissions: + pull-requests: write + contents: write +jobs: + dab-pin-newest: + if: (github.repository_owner == 'ansible' && endsWith(github.repository, 'awx')) || github.event_name != 'schedule' + runs-on: ubuntu-latest + steps: + - id: dab-release + name: Get current django-ansible-base release version + uses: pozetroninc/github-action-get-latest-release@2a61c339ea7ef0a336d1daa35ef0cb1418e7676c # v0.8.0 + with: + owner: ansible + repo: django-ansible-base + excludes: prerelease, draft + + - name: Check out respository code + uses: actions/checkout@v4 + + - id: dab-pinned + name: Get current django-ansible-base pinned version + run: + echo "version=$(requirements/django-ansible-base-pinned-version.sh)" >> "$GITHUB_OUTPUT" + + - name: Update django-ansible-base pinned version to upstream release + run: + requirements/django-ansible-base-pinned-version.sh -s ${{ steps.dab-release.outputs.release }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6 + with: + base: devel + branch: bump-django-ansible-base + title: Bump django-ansible-base to ${{ steps.dab-release.outputs.release }} + body: | + ##### SUMMARY + Automated .github/workflows/dab-release.yml + + django-ansible-base upstream released version == ${{ steps.dab-release.outputs.release }} + requirements_git.txt django-ansible-base pinned version == ${{ steps.dab-pinned.outputs.version }} + + ##### ISSUE TYPE + - Bug, Docs Fix or other nominal change + + ##### COMPONENT NAME + - API + + commit-message: | + Update django-ansible-base version to ${{ steps.dab-pinned.outputs.version }} + add-paths: + requirements/requirements_git.txt diff --git a/.github/workflows/devel_images.yml b/.github/workflows/devel_images.yml index dbc1a4937bee..213d90b8eca9 100644 --- a/.github/workflows/devel_images.yml +++ b/.github/workflows/devel_images.yml @@ -2,44 +2,78 @@ name: Build/Push Development Images env: LC_ALL: "C.UTF-8" # prevent ERROR: Ansible could not initialize the preferred locale: unsupported locale setting + DOCKER_CACHE: "--no-cache" # using the cache will not rebuild git requirements and other things on: + workflow_dispatch: push: branches: - devel - release_* + - feature_* + - stable-* jobs: - push: - if: endsWith(github.repository, '/awx') || startsWith(github.ref, 'refs/heads/release_') + push-development-images: runs-on: ubuntu-latest + timeout-minutes: 120 permissions: packages: write contents: read + strategy: + fail-fast: false + matrix: + build-targets: + - image-name: awx_devel + make-target: docker-compose-buildx + - image-name: awx_kube_devel + make-target: awx-kube-dev-buildx + - image-name: awx + make-target: awx-kube-buildx steps: - - uses: actions/checkout@v2 - - name: Get python version from Makefile - run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV + - name: Skipping build of awx image for non-awx repository + run: | + echo "Skipping build of awx image for non-awx repository" + exit 0 + if: matrix.build-targets.image-name == 'awx' && !endsWith(github.repository, '/awx') - - name: Install python ${{ env.py_version }} - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 with: - python-version: ${{ env.py_version }} + show-progress: false + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set GITHUB_ENV variables + run: | + echo "DEV_DOCKER_TAG_BASE=ghcr.io/${OWNER,,}" >> $GITHUB_ENV + echo "COMPOSE_TAG=${GITHUB_REF##*/}" >> $GITHUB_ENV + env: + OWNER: '${{ github.repository_owner }}' + + - uses: ./.github/actions/setup-python - name: Log in to registry run: | echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - name: Pre-pull image to warm build cache - run: | - docker pull ghcr.io/${{ github.repository_owner }}/awx_devel:${GITHUB_REF##*/} || : - docker pull ghcr.io/${{ github.repository_owner }}/awx_kube_devel:${GITHUB_REF##*/} || : + - name: Setup node and npm for the new UI build + uses: actions/setup-node@v2 + with: + node-version: '18' + if: matrix.build-targets.image-name == 'awx' - - name: Build images + - name: Prebuild new UI for awx image (to speed up build process) run: | - DEV_DOCKER_TAG_BASE=ghcr.io/${{ github.repository_owner }} COMPOSE_TAG=${GITHUB_REF##*/} make docker-compose-build - DEV_DOCKER_TAG_BASE=ghcr.io/${{ github.repository_owner }} COMPOSE_TAG=${GITHUB_REF##*/} make awx-kube-dev-build + make ui + if: matrix.build-targets.image-name == 'awx' + + - uses: ./.github/actions/setup-ssh-agent + with: + ssh-private-key: ${{ secrets.PRIVATE_GITHUB_KEY }} - - name: Push image + - name: Build and push AWX devel images run: | - docker push ghcr.io/${{ github.repository_owner }}/awx_devel:${GITHUB_REF##*/} - docker push ghcr.io/${{ github.repository_owner }}/awx_kube_devel:${GITHUB_REF##*/} + make ${{ matrix.build-targets.make-target }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000000..ec6c9f4a4f2f --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,23 @@ +--- +name: Docsite CI +on: + pull_request: +jobs: + docsite-build: + name: docsite test build + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + with: + show-progress: false + + - uses: ./.github/actions/setup-python + with: + python-version: '3.x' + + - name: install tox + run: pip install tox + + - name: Assure docs can be built + run: tox -e docs diff --git a/.github/workflows/e2e_test.yml b/.github/workflows/e2e_test.yml deleted file mode 100644 index 788042baed97..000000000000 --- a/.github/workflows/e2e_test.yml +++ /dev/null @@ -1,109 +0,0 @@ ---- -name: E2E Tests -env: - LC_ALL: "C.UTF-8" # prevent ERROR: Ansible could not initialize the preferred locale: unsupported locale setting - -on: - pull_request_target: - types: [labeled] -jobs: - e2e-test: - if: contains(github.event.pull_request.labels.*.name, 'qe:e2e') - runs-on: ubuntu-latest - timeout-minutes: 40 - permissions: - packages: write - contents: read - strategy: - matrix: - job: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] - - steps: - - uses: actions/checkout@v2 - - - name: Get python version from Makefile - run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV - - - name: Install python ${{ env.py_version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ env.py_version }} - - - name: Install system deps - run: sudo apt-get install -y gettext - - - name: Log in to registry - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - - name: Pre-pull image to warm build cache - run: | - docker pull ghcr.io/${{ github.repository_owner }}/awx_devel:${{ github.base_ref }} - - - name: Build UI - run: | - DEV_DOCKER_TAG_BASE=ghcr.io/${{ github.repository_owner }} COMPOSE_TAG=${{ github.base_ref }} make ui-devel - - - name: Start AWX - run: | - DEV_DOCKER_TAG_BASE=ghcr.io/${{ github.repository_owner }} COMPOSE_TAG=${{ github.base_ref }} make docker-compose &> make-docker-compose-output.log & - - - name: Pull awx_cypress_base image - run: | - docker pull quay.io/awx/awx_cypress_base:latest - - - name: Checkout test project - uses: actions/checkout@v2 - with: - repository: ${{ github.repository_owner }}/tower-qa - ssh-key: ${{ secrets.QA_REPO_KEY }} - path: tower-qa - ref: devel - - - name: Build cypress - run: | - cd ${{ secrets.E2E_PROJECT }}/ui-tests/awx-pf-tests - docker build -t awx-pf-tests . - - - name: Update default AWX password - run: | - while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' -k https://localhost:8043/api/v2/ping/)" != "200" ]] - do - echo "Waiting for AWX..." - sleep 5; - done - echo "AWX is up, updating the password..." - docker exec -i tools_awx_1 sh <<-EOSH - awx-manage update_password --username=admin --password=password - EOSH - - - name: Run E2E tests - env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - run: | - export COMMIT_INFO_BRANCH=$GITHUB_HEAD_REF - export COMMIT_INFO_AUTHOR=$GITHUB_ACTOR - export COMMIT_INFO_SHA=$GITHUB_SHA - export COMMIT_INFO_REMOTE=$GITHUB_REPOSITORY_OWNER - cd ${{ secrets.E2E_PROJECT }}/ui-tests/awx-pf-tests - AWX_IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tools_awx_1) - printenv > .env - echo "Executing tests:" - docker run \ - --network '_sources_default' \ - --ipc=host \ - --env-file=.env \ - -e CYPRESS_baseUrl="https://$AWX_IP:8043" \ - -e CYPRESS_AWX_E2E_USERNAME=admin \ - -e CYPRESS_AWX_E2E_PASSWORD='password' \ - -e COMMAND="npm run cypress-concurrently-gha" \ - -v /dev/shm:/dev/shm \ - -v $PWD:/e2e \ - -w /e2e \ - awx-pf-tests run --project . - - - name: Save AWX logs - uses: actions/upload-artifact@v2 - with: - name: AWX-logs-${{ matrix.job }} - path: make-docker-compose-output.log diff --git a/.github/workflows/feature_branch_deletion.yml b/.github/workflows/feature_branch_deletion.yml index 3e574b211b80..0de807aaf4f8 100644 --- a/.github/workflows/feature_branch_deletion.yml +++ b/.github/workflows/feature_branch_deletion.yml @@ -2,13 +2,12 @@ name: Feature branch deletion cleanup env: LC_ALL: "C.UTF-8" # prevent ERROR: Ansible could not initialize the preferred locale: unsupported locale setting -on: - delete: - branches: - - feature_** +on: delete jobs: - push: + branch_delete: + if: ${{ github.event.ref_type == 'branch' && startsWith(github.event.ref, 'feature_') }} runs-on: ubuntu-latest + timeout-minutes: 20 permissions: packages: write contents: read @@ -21,6 +20,4 @@ jobs: run: | ansible localhost -c local, -m command -a "{{ ansible_python_interpreter + ' -m pip install boto3'}}" ansible localhost -c local -m aws_s3 \ - -a "bucket=awx-public-ci-files object=${GITHUB_REF##*/}/schema.json mode=delete permission=public-read" - - + -a "bucket=awx-public-ci-files object=${{ github.event.repository.name }}/${GITHUB_REF##*/}/schema.json mode=delobj permission=public-read" diff --git a/.github/workflows/label_issue.yml b/.github/workflows/label_issue.yml index ead15724bbc0..952685cd5476 100644 --- a/.github/workflows/label_issue.yml +++ b/.github/workflows/label_issue.yml @@ -6,14 +6,19 @@ on: - opened - reopened +permissions: + contents: write # to fetch code + issues: write # to label issues + jobs: triage: runs-on: ubuntu-latest + timeout-minutes: 20 name: Label Issue steps: - name: Label Issue - uses: github/issue-labeler@v2.4.1 + uses: github/issue-labeler@v3.1 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" not-before: 2021-12-07T07:00:00Z @@ -22,12 +27,18 @@ jobs: community: runs-on: ubuntu-latest + timeout-minutes: 20 name: Label Issue - Community steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + with: + show-progress: false + + - uses: ./.github/actions/setup-python + - name: Install python requests run: pip install requests + - name: Check if user is a member of Ansible org uses: jannekem/run-python-script-action@v1 id: check_user diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml index 8e3f8b81a210..43f1e3a2915c 100644 --- a/.github/workflows/label_pr.yml +++ b/.github/workflows/label_pr.yml @@ -7,9 +7,14 @@ on: - reopened - synchronize +permissions: + contents: write # to determine modified files (actions/labeler) + pull-requests: write # to add labels to PRs (actions/labeler) + jobs: triage: runs-on: ubuntu-latest + timeout-minutes: 20 name: Label PR steps: @@ -21,10 +26,17 @@ jobs: community: runs-on: ubuntu-latest + timeout-minutes: 20 name: Label PR - Community steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + with: + show-progress: false + + - uses: ./.github/actions/setup-python + with: + python-version: '3.x' + - name: Install python requests run: pip install requests - name: Check if user is a member of Ansible org diff --git a/.github/workflows/pr_body_check.yml b/.github/workflows/pr_body_check.yml index 7ddcabd3d64b..9532aa87ede1 100644 --- a/.github/workflows/pr_body_check.yml +++ b/.github/workflows/pr_body_check.yml @@ -7,8 +7,10 @@ on: types: [opened, edited, reopened, synchronize] jobs: pr-check: + if: github.repository_owner == 'ansible' && endsWith(github.repository, 'awx') name: Scan PR description for semantic versioning keywords runs-on: ubuntu-latest + timeout-minutes: 20 permissions: packages: write contents: read diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml index 6bd36f010200..ba723c07f975 100644 --- a/.github/workflows/promote.yml +++ b/.github/workflows/promote.yml @@ -7,21 +7,36 @@ env: on: release: types: [published] + workflow_dispatch: + inputs: + tag_name: + description: 'Name for the tag of the release.' + required: true +permissions: + contents: read # to fetch code (actions/checkout) jobs: promote: + if: endsWith(github.repository, '/awx') runs-on: ubuntu-latest + timeout-minutes: 90 steps: - - name: Checkout awx - uses: actions/checkout@v2 + - name: Set GitHub Env vars for workflow_dispatch event + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV - - name: Get python version from Makefile - run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV + - name: Set GitHub Env vars if release event + if: ${{ github.event_name == 'release' }} + run: | + echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV - - name: Install python ${{ env.py_version }} - uses: actions/setup-python@v2 + - name: Checkout awx + uses: actions/checkout@v4 with: - python-version: ${{ env.py_version }} + show-progress: false + + - uses: ./.github/actions/setup-python - name: Install dependencies run: | @@ -36,14 +51,23 @@ jobs: if: ${{ github.repository_owner != 'ansible' }} - name: Build collection and publish to galaxy + env: + COLLECTION_NAMESPACE: ${{ env.collection_namespace }} + COLLECTION_VERSION: ${{ env.TAG_NAME }} + COLLECTION_TEMPLATE_VERSION: true run: | - COLLECTION_TEMPLATE_VERSION=true COLLECTION_NAMESPACE=${{ env.collection_namespace }} make build_collection - if [ "$(curl --head -sw '%{http_code}' https://galaxy.ansible.com/download/${{ env.collection_namespace }}-awx-${{ github.event.release.tag_name }}.tar.gz | tail -1)" == "302" ] ; then \ - echo "Galaxy release already done"; \ - else \ + sudo apt-get install jq + make build_collection + count=$(curl -s https://galaxy.ansible.com/api/v3/plugin/ansible/search/collection-versions/\?namespace\=${COLLECTION_NAMESPACE}\&name\=awx\&version\=${COLLECTION_VERSION} | jq .meta.count) + if [[ "$count" == "1" ]]; then + echo "Galaxy release already done"; + elif [[ "$count" == "0" ]]; then ansible-galaxy collection publish \ --token=${{ secrets.GALAXY_TOKEN }} \ - awx_collection_build/${{ env.collection_namespace }}-awx-${{ github.event.release.tag_name }}.tar.gz; \ + awx_collection_build/${COLLECTION_NAMESPACE}-awx-${COLLECTION_VERSION}.tar.gz; + else + echo "Unexpected count from galaxy search: $count"; + exit 1; fi - name: Set official pypi info @@ -55,9 +79,11 @@ jobs: if: ${{ github.repository_owner != 'ansible' }} - name: Build awxkit and upload to pypi + env: + SETUPTOOLS_SCM_PRETEND_VERSION: ${{ env.TAG_NAME }} run: | git reset --hard - cd awxkit && python3 setup.py bdist_wheel + cd awxkit && python3 setup.py sdist bdist_wheel twine upload \ -r ${{ env.pypi_repo }} \ -u ${{ secrets.PYPI_USERNAME }} \ @@ -74,11 +100,15 @@ jobs: - name: Re-tag and promote awx image run: | - docker pull ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }} - docker tag ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }} quay.io/${{ github.repository }}:${{ github.event.release.tag_name }} - docker tag ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }} quay.io/${{ github.repository }}:latest - docker push quay.io/${{ github.repository }}:${{ github.event.release.tag_name }} - docker push quay.io/${{ github.repository }}:latest - docker pull ghcr.io/${{ github.repository_owner }}/awx-ee:${{ github.event.release.tag_name }} - docker tag ghcr.io/${{ github.repository_owner }}/awx-ee:${{ github.event.release.tag_name }} quay.io/${{ github.repository_owner }}/awx-ee:${{ github.event.release.tag_name }} - docker push quay.io/${{ github.repository_owner }}/awx-ee:${{ github.event.release.tag_name }} + docker buildx imagetools create \ + ghcr.io/${{ github.repository }}:${{ env.TAG_NAME }} \ + --tag quay.io/${{ github.repository }}:${{ env.TAG_NAME }} + docker buildx imagetools create \ + ghcr.io/${{ github.repository }}:${{ env.TAG_NAME }} \ + --tag quay.io/${{ github.repository }}:latest + + - name: Re-tag and promote awx-ee image + run: | + docker buildx imagetools create \ + ghcr.io/${{ github.repository_owner }}/awx-ee:${{ env.TAG_NAME }} \ + --tag quay.io/${{ github.repository_owner }}/awx-ee:${{ env.TAG_NAME }} diff --git a/.github/workflows/sonarcloud_pr.yml b/.github/workflows/sonarcloud_pr.yml new file mode 100644 index 000000000000..380118d7b5c4 --- /dev/null +++ b/.github/workflows/sonarcloud_pr.yml @@ -0,0 +1,248 @@ +# SonarCloud Analysis Workflow for awx +# +# This workflow runs SonarCloud analysis triggered by CI workflow completion. +# It is split into two separate jobs for clarity and maintainability: +# +# FLOW: CI completes → workflow_run triggers this workflow → appropriate job runs +# +# JOB 1: sonar-pr-analysis (for PRs) +# - Triggered by: workflow_run (CI on pull_request) +# - Steps: Download coverage → Get PR info → Get changed files → Run SonarCloud PR analysis +# - Scans: All changed files in the PR (Python, YAML, JSON, etc.) +# - Quality gate: Focuses on new/changed code in PR only +# +# JOB 2: sonar-branch-analysis (for long-lived branches) +# - Triggered by: workflow_run (CI on push to devel) +# - Steps: Download coverage → Run SonarCloud branch analysis +# - Scans: Full codebase +# - Quality gate: Focuses on overall project health +# +# This ensures coverage data is always available from CI before analysis runs. +# +# What files are scanned: +# - All files in the repository that SonarCloud can analyze +# - Excludes: tests, scripts, dev environments, external collections (see sonar-project.properties) + + +# With much help from: +# https://community.sonarsource.com/t/how-to-use-sonarcloud-with-a-forked-repository-on-github/7363/30 +# https://community.sonarsource.com/t/how-to-use-sonarcloud-with-a-forked-repository-on-github/7363/32 +name: SonarCloud +on: + workflow_run: # This is triggered by CI being completed. + workflows: + - CI + types: + - completed +permissions: read-all +jobs: + sonar-pr-analysis: + name: SonarCloud PR Analysis + runs-on: ubuntu-latest + if: | + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'pull_request' && + github.repository == 'ansible/awx' + steps: + - uses: actions/checkout@v4 + + # Download all individual coverage artifacts from CI workflow + - name: Download coverage artifacts + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + workflow: CI + run_id: ${{ github.event.workflow_run.id }} + pattern: api-test-artifacts + + # Extract PR metadata from workflow_run event + - name: Set PR metadata and prepare files for analysis + env: + COMMIT_SHA: ${{ github.event.workflow_run.head_sha }} + REPO_NAME: ${{ github.event.repository.full_name }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Find all downloaded coverage XML files + coverage_files=$(find . -name "coverage.xml" -type f | tr '\n' ',' | sed 's/,$//') + echo "Found coverage files: $coverage_files" + echo "COVERAGE_PATHS=$coverage_files" >> $GITHUB_ENV + + # Extract PR number from first coverage.xml file found + first_coverage=$(find . -name "coverage.xml" -type f | head -1) + if [ -f "$first_coverage" ]; then + PR_NUMBER=$(grep -m 1 ' host_id + # so we can associate emitted events to Host objects + self.runner_callback.host_map[hostname] = hv.get('remote_tower_id', '') + file_content = '#! /usr/bin/env python3\n# -*- coding: utf-8 -*-\nprint(%r)\n' % json.dumps(script_data) + return self.write_private_data_file(private_data_dir, file_name, file_content, sub_dir='inventory', file_permissions=0o700) + def build_inventory(self, instance, private_data_dir): script_params = dict(hostvars=True, towervars=True) if hasattr(instance, 'job_slice_number'): script_params['slice_number'] = instance.job_slice_number script_params['slice_count'] = instance.job_slice_count - script_data = instance.inventory.get_script_data(**script_params) - # maintain a list of host_name --> host_id - # so we can associate emitted events to Host objects - self.runner_callback.host_map = {hostname: hv.pop('remote_tower_id', '') for hostname, hv in script_data.get('_meta', {}).get('hostvars', {}).items()} - file_content = '#! /usr/bin/env python3\n# -*- coding: utf-8 -*-\nprint(%r)\n' % json.dumps(script_data) - return self.write_private_data_file(private_data_dir, 'hosts', file_content, sub_dir='inventory', file_permissions=0o700) + + return self.write_inventory_file(instance.inventory, private_data_dir, 'hosts', script_params) def build_args(self, instance, private_data_dir, passwords): raise NotImplementedError @@ -337,6 +510,7 @@ def build_credentials_list(self, instance): return [] def get_instance_timeout(self, instance): + """Return the effective job timeout in seconds.""" global_timeout_setting_name = instance._global_timeout_setting() if global_timeout_setting_name: global_timeout = getattr(settings, global_timeout_setting_name, 0) @@ -399,7 +573,7 @@ def acquire_lock(self, project, unified_job_id=None): except IOError as e: if e.errno not in (errno.EAGAIN, errno.EACCES): os.close(self.lock_fd) - logger.error("I/O error({0}) while trying to aquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror)) + logger.error("I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror)) raise else: if not emitted_lockfile_log: @@ -435,20 +609,15 @@ def final_run_hook(self, instance, status, private_data_dir): Hook for any steps to run after job/task is marked as complete. """ instance.log_lifecycle("finalize_run") - artifact_dir = os.path.join(private_data_dir, 'artifacts', str(self.instance.id)) - collections_info = os.path.join(artifact_dir, 'collections.json') - ansible_version_file = os.path.join(artifact_dir, 'ansible_version.txt') - - if os.path.exists(collections_info): - with open(collections_info) as ee_json_info: - ee_collections_info = json.loads(ee_json_info.read()) - instance.installed_collections = ee_collections_info - instance.save(update_fields=['installed_collections']) - if os.path.exists(ansible_version_file): - with open(ansible_version_file) as ee_ansible_info: - ansible_version_info = ee_ansible_info.readline() - instance.ansible_version = ansible_version_info - instance.save(update_fields=['ansible_version']) + + # Run task manager appropriately for speculative dependencies + if instance.unifiedjob_blocked_jobs.exists(): + ScheduleTaskManager().schedule() + if instance.spawned_by_workflow: + ScheduleWorkflowManager().schedule() + + def should_use_fact_cache(self): + return False @with_path_cleanup @with_signal_handling @@ -456,21 +625,26 @@ def run(self, pk, **kwargs): """ Run the job/task and capture its output. """ - self.instance = self.model.objects.get(pk=pk) - if self.instance.status != 'canceled' and self.instance.cancel_flag: - self.instance = self.update_model(self.instance.pk, start_args='', status='canceled') - if self.instance.status not in ACTIVE_STATES: - # Prevent starting the job if it has been reaped or handled by another process. - raise RuntimeError(f'Not starting {self.instance.status} task pk={pk} because {self.instance.status} is not a valid active state') - if self.instance.execution_environment_id is None: - from awx.main.signals import disable_activity_stream + if not self.instance: # Used to skip fetch for local runs + # Load the instance + self.instance = self.update_model(pk) - with disable_activity_stream(): - self.instance = self.update_model(self.instance.pk, execution_environment=self.instance.resolve_execution_environment()) + # status should be "running" from dispatch_waiting_jobs, + # but may still be "waiting" if the worker picked this up before the status update landed. + if self.instance.status == 'waiting': + UnifiedJob.objects.filter(pk=pk).update(status="running", start_args='') + self.instance.refresh_from_db() + + if self.instance.status != 'running': + logger.error(f'Not starting {self.instance.status} task pk={pk} because its status "{self.instance.status}" is not expected') + return + + if self.instance.cancel_flag: + self.instance = self.update_model(pk, status='canceled') + self.instance.websocket_emit_status('canceled') + return - # self.instance because of the update_model pattern and when it's used in callback handlers - self.instance = self.update_model(pk, status='running', start_args='') # blank field to remove encrypted passwords self.instance.websocket_emit_status("running") status, rc = 'error', None self.runner_callback.event_ct = 0 @@ -483,12 +657,20 @@ def run(self, pk, **kwargs): private_data_dir = None try: + if self.instance.execution_environment_id is None: + from awx.main.signals import disable_activity_stream + + with disable_activity_stream(): + self.instance = self.update_model(self.instance.pk, execution_environment=self.instance.resolve_execution_environment()) + self.instance.send_notification_templates("running") private_data_dir = self.build_private_data_dir(self.instance) self.pre_run_hook(self.instance, private_data_dir) + evaluate_policy(self.instance) self.build_project_dir(self.instance, private_data_dir) self.instance.log_lifecycle("preparing_playbook") if self.instance.cancel_flag or signal_callback(): + logger.debug(f'detected pre-run cancel flag for {self.instance.log_format}') self.instance = self.update_model(self.instance.pk, status='canceled') if self.instance.status != 'running': @@ -501,6 +683,12 @@ def run(self, pk, **kwargs): if not os.path.exists(settings.AWX_ISOLATION_BASE_PATH): raise RuntimeError('AWX_ISOLATION_BASE_PATH=%s does not exist' % settings.AWX_ISOLATION_BASE_PATH) + if flag_enabled("FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED"): + logger.info(f'Generating workload identity tokens for {self.instance.log_format}') + self.populate_workload_identity_tokens() + if self.instance.status == 'error': + raise RuntimeError('not starting %s task' % self.instance.status) + # May have to serialize the value private_data_files, ssh_key_data = self.build_private_data_files(self.instance, private_data_dir) passwords = self.build_passwords(self.instance, kwargs) @@ -518,11 +706,15 @@ def run(self, pk, **kwargs): self.runner_callback.job_created = str(self.instance.created) - credentials = self.build_credentials_list(self.instance) + credentials = self._credentials + + container_root = None + if settings.IS_K8S and isinstance(self.instance, ProjectUpdate): + container_root = private_data_dir for credential in credentials: if credential: - credential.credential_type.inject_credential(credential, env, self.safe_cred_env, args, private_data_dir) + credential.credential_type.inject_credential(credential, env, self.safe_cred_env, args, private_data_dir, container_root=container_root) self.runner_callback.safe_env.update(self.safe_cred_env) @@ -548,7 +740,8 @@ def run(self, pk, **kwargs): params['module'] = self.build_module_name(self.instance) params['module_args'] = self.build_module_args(self.instance) - if getattr(self.instance, 'use_fact_cache', False): + # TODO: refactor into a better BasTask method + if self.should_use_fact_cache(): # Enable Ansible fact cache. params['fact_cache_type'] = 'jsonfile' else: @@ -606,12 +799,11 @@ def run(self, pk, **kwargs): elif status == 'canceled': self.instance = self.update_model(pk) cancel_flag_value = getattr(self.instance, 'cancel_flag', False) - if (cancel_flag_value is False) and signal_callback(): + if cancel_flag_value is False: self.runner_callback.delay_update(skip_if_already_set=True, job_explanation="Task was canceled due to receiving a shutdown signal.") status = 'failed' - elif cancel_flag_value is False: - self.runner_callback.delay_update(skip_if_already_set=True, job_explanation="The running ansible process received a shutdown signal.") - status = 'failed' + except PolicyEvaluationError as exc: + self.runner_callback.delay_update(job_explanation=str(exc), result_traceback=str(exc)) except ReceptorNodeNotFound as exc: self.runner_callback.delay_update(job_explanation=str(exc)) except Exception: @@ -637,8 +829,11 @@ def run(self, pk, **kwargs): # Field host_status_counts is used as a metric to check if event processing is finished # we send notifications if it is, if not, callback receiver will send them + if not self.instance: + logger.error(f'Unified job pk={pk} appears to be deleted while running') + return if (self.instance.host_status_counts is not None) or (not self.runner_callback.wrapup_event_dispatched): - self.instance.send_notification_templates('succeeded' if status == 'successful' else 'failed') + events_processed_hook(self.instance) try: self.final_run_hook(self.instance, status, private_data_dir) @@ -686,6 +881,7 @@ def get_sync_needs(self, project, scm_branch=None): logger.debug(f'Project not available locally, {self.instance.id} will sync with remote') sync_needs.append(source_update_tag) + # Determine whether or not this project sync needs to populate the cache for Ansible content, roles and collections has_cache = os.path.exists(os.path.join(project.get_cache_path(), project.cache_id)) # Galaxy requirements are not supported for manual projects if project.scm_type and ((not has_cache) or branch_override): @@ -732,6 +928,7 @@ def sync_and_copy_without_lock(self, project, private_data_dir, scm_branch=None) try: # the job private_data_dir is passed so sync can download roles and collections there sync_task = RunProjectUpdate(job_private_data_dir=private_data_dir) + sync_task.instance = local_project_sync # avoids "waiting" status check, performance sync_task.run(local_project_sync.id) local_project_sync.refresh_from_db() self.instance = self.update_model(self.instance.pk, scm_revision=local_project_sync.scm_revision) @@ -759,7 +956,7 @@ def sync_and_copy_without_lock(self, project, private_data_dir, scm_branch=None) def sync_and_copy(self, project, private_data_dir, scm_branch=None): self.acquire_lock(project, self.instance.id) - + is_commit = False try: original_branch = None failed_reason = project.get_reason_if_failed() @@ -771,6 +968,7 @@ def sync_and_copy(self, project, private_data_dir, scm_branch=None): if os.path.exists(project_path): git_repo = git.Repo(project_path) if git_repo.head.is_detached: + is_commit = True original_branch = git_repo.head.commit else: original_branch = git_repo.active_branch @@ -782,7 +980,11 @@ def sync_and_copy(self, project, private_data_dir, scm_branch=None): # for git project syncs, non-default branches can be problems # restore to branch the repo was on before this run try: - original_branch.checkout() + if is_commit: + git_repo.head.set_commit(original_branch) + git_repo.head.reset(index=True, working_tree=True) + else: + original_branch.checkout() except Exception: # this could have failed due to dirty tree, but difficult to predict all cases logger.exception(f'Failed to restore project repo to prior state after {self.instance.id}') @@ -790,7 +992,7 @@ def sync_and_copy(self, project, private_data_dir, scm_branch=None): self.release_lock(project) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename) class RunJob(SourceControlMixin, BaseTask): """ Run a job using ansible-playbook. @@ -799,6 +1001,29 @@ class RunJob(SourceControlMixin, BaseTask): model = Job event_model = JobEvent + def _extract_credentials_of_kind(self, kind: str): + return (cred for cred in self._credentials if cred.credential_type.kind == kind) + + @property + def _machine_credential(self) -> object: + """Get machine credential.""" + return next(self._extract_credentials_of_kind('ssh'), None) + + @property + def _vault_credentials(self) -> list[object]: + """Get vault credentials.""" + return list(self._extract_credentials_of_kind('vault')) + + @property + def _network_credentials(self) -> list[object]: + """Get network credentials.""" + return list(self._extract_credentials_of_kind('net')) + + @property + def _cloud_credentials(self) -> list[object]: + """Get cloud credentials.""" + return list(self._extract_credentials_of_kind('cloud')) + def build_private_data(self, job, private_data_dir): """ Returns a dict of the form @@ -816,7 +1041,7 @@ def build_private_data(self, job, private_data_dir): } """ private_data = {'credentials': {}} - for credential in job.credentials.prefetch_related('input_sources__source_credential').all(): + for credential in self._credentials: # If we were sent SSH credentials, decrypt them and send them # back (they will be written to a temporary file). if credential.has_input('ssh_key_data'): @@ -832,14 +1057,14 @@ def build_passwords(self, job, runtime_passwords): and ansible-vault. """ passwords = super(RunJob, self).build_passwords(job, runtime_passwords) - cred = job.machine_credential + cred = self._machine_credential if cred: for field in ('ssh_key_unlock', 'ssh_password', 'become_password', 'vault_password'): value = runtime_passwords.get(field, cred.get_input('password' if field == 'ssh_password' else field, default='')) if value not in ('', 'ASK'): passwords[field] = value - for cred in job.vault_credentials: + for cred in self._vault_credentials: field = 'vault_password' vault_id = cred.get_input('vault_id', default=None) if vault_id: @@ -855,7 +1080,7 @@ def build_passwords(self, job, runtime_passwords): key unlock over network key unlock. ''' if 'ssh_key_unlock' not in passwords: - for cred in job.network_credentials: + for cred in self._network_credentials: if cred.inputs.get('ssh_key_unlock'): passwords['ssh_key_unlock'] = runtime_passwords.get('ssh_key_unlock', cred.get_input('ssh_key_unlock', default='')) break @@ -890,11 +1115,11 @@ def build_env(self, job, private_data_dir, private_data_files=None): # Set environment variables for cloud credentials. cred_files = private_data_files.get('credentials', {}) - for cloud_cred in job.cloud_credentials: + for cloud_cred in self._cloud_credentials: if cloud_cred and cloud_cred.credential_type.namespace == 'openstack' and cred_files.get(cloud_cred, ''): - env['OS_CLIENT_CONFIG_FILE'] = to_container_path(cred_files.get(cloud_cred, ''), private_data_dir) + env['OS_CLIENT_CONFIG_FILE'] = get_incontainer_path(cred_files.get(cloud_cred, ''), private_data_dir) - for network_cred in job.network_credentials: + for network_cred in self._network_credentials: env['ANSIBLE_NET_USERNAME'] = network_cred.get_input('username', default='') env['ANSIBLE_NET_PASSWORD'] = network_cred.get_input('password', default='') @@ -907,10 +1132,15 @@ def build_env(self, job, private_data_dir, private_data_files=None): if authorize: env['ANSIBLE_NET_AUTH_PASS'] = network_cred.get_input('authorize_password', default='') - path_vars = ( - ('ANSIBLE_COLLECTIONS_PATHS', 'collections_paths', 'requirements_collections', '~/.ansible/collections:/usr/share/ansible/collections'), + path_vars = [ ('ANSIBLE_ROLES_PATH', 'roles_path', 'requirements_roles', '~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles'), - ) + ('ANSIBLE_COLLECTIONS_PATH', 'collections_path', 'requirements_collections', '~/.ansible/collections:/usr/share/ansible/collections'), + ] + + if flag_enabled("FEATURE_INDIRECT_NODE_COUNTING_ENABLED"): + path_vars.append( + ('ANSIBLE_CALLBACK_PLUGINS', 'callback_plugins', 'plugins_path', '~/.ansible/plugins:/plugins/callback:/usr/share/ansible/plugins/callback'), + ) config_values = read_ansible_config(os.path.join(private_data_dir, 'project'), list(map(lambda x: x[1], path_vars))) @@ -927,6 +1157,16 @@ def build_env(self, job, private_data_dir, private_data_files=None): paths = [os.path.join(CONTAINER_ROOT, folder)] + paths env[env_key] = os.pathsep.join(paths) + if flag_enabled("FEATURE_INDIRECT_NODE_COUNTING_ENABLED"): + env['ANSIBLE_CALLBACKS_ENABLED'] = 'indirect_instance_count' + if 'callbacks_enabled' in config_values: + env['ANSIBLE_CALLBACKS_ENABLED'] += ':' + config_values['callbacks_enabled'] + + # Add vendor collections path for external query file discovery + vendor_collections_path = os.path.join(CONTAINER_ROOT, 'vendor_collections') + env['ANSIBLE_COLLECTIONS_PATH'] = f"{vendor_collections_path}:{env['ANSIBLE_COLLECTIONS_PATH']}" + logger.debug(f"ANSIBLE_COLLECTIONS_PATH updated for vendor collections: {env['ANSIBLE_COLLECTIONS_PATH']}") + return env def build_args(self, job, private_data_dir, passwords): @@ -934,7 +1174,7 @@ def build_args(self, job, private_data_dir, passwords): Build command line argument list for running ansible-playbook, optionally using ssh-agent for public/private key authentication. """ - creds = job.machine_credential + creds = self._machine_credential ssh_username, become_username, become_method = '', '', '' if creds: @@ -998,6 +1238,9 @@ def build_args(self, job, private_data_dir, passwords): return args + def should_use_fact_cache(self): + return self.instance.use_fact_cache + def build_playbook_path_relative_to_cwd(self, job, private_data_dir): return job.playbook @@ -1063,8 +1306,11 @@ def pre_run_hook(self, job, private_data_dir): # Fetch "cached" fact data from prior runs and put on the disk # where ansible expects to find it - if job.use_fact_cache: - self.facts_write_time = self.instance.start_job_fact_cache(os.path.join(private_data_dir, 'artifacts', str(job.id), 'fact_cache')) + if self.should_use_fact_cache(): + job.log_lifecycle("start_job_fact_cache") + self.hosts_with_facts_cached = start_fact_cache( + job.get_hosts_for_fact_cache(), artifacts_dir=os.path.join(private_data_dir, 'artifacts', str(job.id)), inventory_id=job.inventory_id + ) def build_project_dir(self, job, private_data_dir): self.sync_and_copy(job.project, private_data_dir, scm_branch=job.scm_branch) @@ -1073,15 +1319,23 @@ def post_run_hook(self, job, status): super(RunJob, self).post_run_hook(job, status) job.refresh_from_db(fields=['job_env']) private_data_dir = job.job_env.get('AWX_PRIVATE_DATA_DIR') - if (not private_data_dir) or (not hasattr(self, 'facts_write_time')): + if not private_data_dir: # If there's no private data dir, that means we didn't get into the # actual `run()` call; this _usually_ means something failed in # the pre_run_hook method return - if job.use_fact_cache: - job.finish_job_fact_cache( - os.path.join(private_data_dir, 'artifacts', str(job.id), 'fact_cache'), - self.facts_write_time, + if self.should_use_fact_cache() and self.runner_callback.artifacts_processed: + job.log_lifecycle("finish_job_fact_cache") + if job.inventory.kind == 'constructed': + hosts_qs = job.get_source_hosts_for_constructed_inventory() + else: + hosts_qs = job.inventory.hosts + finish_fact_cache( + hosts_qs, + artifacts_dir=os.path.join(private_data_dir, 'artifacts', str(job.id)), + job_id=job.id, + inventory_id=job.inventory_id, + job_created=job.created, ) def final_run_hook(self, job, status, private_data_dir): @@ -1095,7 +1349,7 @@ def final_run_hook(self, job, status, private_data_dir): update_inventory_computed_fields.delay(inventory.id) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename) class RunProjectUpdate(BaseTask): model = ProjectUpdate event_model = ProjectUpdateEvent @@ -1242,7 +1496,7 @@ def build_extra_vars_file(self, project_update, private_data_dir): galaxy_creds_are_defined = project_update.project.organization and project_update.project.organization.galaxy_credentials.exists() if not galaxy_creds_are_defined and (settings.AWX_ROLES_ENABLED or settings.AWX_COLLECTIONS_ENABLED): - logger.warning('Galaxy role/collection syncing is enabled, but no ' f'credentials are configured for {project_update.project.organization}.') + logger.warning(f'Galaxy role/collection syncing is enabled, but no credentials are configured for {project_update.project.organization}.') extra_vars.update( { @@ -1266,7 +1520,7 @@ def build_extra_vars_file(self, project_update, private_data_dir): extra_vars['scm_refspec'] = project_update.scm_refspec elif project_update.project.allow_override: # If branch is override-able, do extra fetch for all branches - extra_vars['scm_refspec'] = 'refs/heads/*:refs/remotes/origin/*' + extra_vars['scm_refspec'] = '+refs/heads/*:refs/remotes/origin/*' if project_update.scm_type == 'archive': # for raw archive, prevent error moving files between volumes @@ -1356,6 +1610,17 @@ def make_local_copy(project, job_private_data_dir): shutil.copytree(cache_subpath, dest_subpath, symlinks=True) logger.debug('{0} {1} prepared {2} from cache'.format(type(project).__name__, project.pk, dest_subpath)) + if flag_enabled("FEATURE_INDIRECT_NODE_COUNTING_ENABLED"): + # copy the special callback (not stdout type) plugin to get list of collections + pdd_plugins_path = os.path.join(job_private_data_dir, 'plugins_path') + if not os.path.exists(pdd_plugins_path): + os.mkdir(pdd_plugins_path) + from awx.playbooks import library + + plugin_file_source = os.path.join(library.__path__._path[0], 'indirect_instance_count.py') + plugin_file_dest = os.path.join(pdd_plugins_path, 'indirect_instance_count.py') + shutil.copyfile(plugin_file_source, plugin_file_dest) + def post_run_hook(self, instance, status): super(RunProjectUpdate, self).post_run_hook(instance, status) # To avoid hangs, very important to release lock even if errors happen here @@ -1416,8 +1681,13 @@ def build_execution_environment_params(self, instance, private_data_dir): ) return params + def build_credentials_list(self, project_update): + if project_update.credential: + return [project_update.credential] + return [] -@task(queue=get_local_queuename) + +@task(queue=get_task_queuename) class RunInventoryUpdate(SourceControlMixin, BaseTask): model = InventoryUpdate event_model = InventoryUpdateEvent @@ -1464,8 +1734,6 @@ def build_env(self, inventory_update, private_data_dir, private_data_files=None) if injector is not None: env = injector.build_env(inventory_update, env, private_data_dir, private_data_files) - # All CLOUD_PROVIDERS sources implement as inventory plugin from collection - env['ANSIBLE_INVENTORY_ENABLED'] = 'auto' if inventory_update.source == 'scm': for env_k in inventory_update.source_vars_dict: @@ -1475,7 +1743,7 @@ def build_env(self, inventory_update, private_data_dir, private_data_files=None) raise NotImplementedError('Cannot update file sources through the task system.') if inventory_update.source == 'scm' and inventory_update.source_project_update: - env_key = 'ANSIBLE_COLLECTIONS_PATHS' + env_key = 'ANSIBLE_COLLECTIONS_PATH' config_setting = 'collections_paths' folder = 'requirements_collections' default = '~/.ansible/collections:/usr/share/ansible/collections' @@ -1493,12 +1761,12 @@ def build_env(self, inventory_update, private_data_dir, private_data_files=None) paths = [config_values[config_setting]] + paths paths = [os.path.join(CONTAINER_ROOT, folder)] + paths env[env_key] = os.pathsep.join(paths) - if 'ANSIBLE_COLLECTIONS_PATHS' in env: - paths = env['ANSIBLE_COLLECTIONS_PATHS'].split(':') + if 'ANSIBLE_COLLECTIONS_PATH' in env: + paths = env['ANSIBLE_COLLECTIONS_PATH'].split(':') else: paths = ['~/.ansible/collections', '/usr/share/ansible/collections'] paths.append('/usr/share/automation-controller/collections') - env['ANSIBLE_COLLECTIONS_PATHS'] = os.pathsep.join(paths) + env['ANSIBLE_COLLECTIONS_PATH'] = os.pathsep.join(paths) return env @@ -1518,6 +1786,22 @@ def build_args(self, inventory_update, private_data_dir, passwords): args = ['ansible-inventory', '--list', '--export'] + # special case for constructed inventories, we pass source inventories from database + # these must come in order, and in order _before_ the constructed inventory itself + if inventory_update.inventory.kind == 'constructed': + inventory_update.log_lifecycle("start_job_fact_cache") + for input_inventory in inventory_update.inventory.input_inventories.all(): + args.append('-i') + script_params = dict(hostvars=True, towervars=True) + source_inv_path = self.write_inventory_file(input_inventory, private_data_dir, f'hosts_{input_inventory.id}', script_params) + args.append(get_incontainer_path(source_inv_path, private_data_dir)) + # Include any facts from input inventories so they can be used in filters + start_fact_cache( + input_inventory.hosts.only(*HOST_FACTS_FIELDS), + artifacts_dir=os.path.join(private_data_dir, 'artifacts', str(inventory_update.id)), + inventory_id=input_inventory.id, + ) + # Add arguments for the source inventory file/script/thing rel_path = self.pseudo_build_inventory(inventory_update, private_data_dir) container_location = os.path.join(CONTAINER_ROOT, rel_path) @@ -1525,6 +1809,11 @@ def build_args(self, inventory_update, private_data_dir, passwords): args.append('-i') args.append(container_location) + # Added this in order to allow older versions of ansible-inventory https://github.com/ansible/ansible/pull/79596 + # limit should be usable in ansible-inventory 2.15+ + if inventory_update.limit: + args.append('--limit') + args.append(inventory_update.limit) args.append('--output') args.append(os.path.join(CONTAINER_ROOT, 'artifacts', str(inventory_update.id), 'output.json')) @@ -1540,6 +1829,9 @@ def build_args(self, inventory_update, private_data_dir, passwords): return args + def should_use_fact_cache(self): + return bool(self.instance.source == 'constructed') + def build_inventory(self, inventory_update, private_data_dir): return None # what runner expects in order to not deal with inventory @@ -1581,7 +1873,7 @@ def build_project_dir(self, inventory_update, private_data_dir): if inventory_update.source == 'scm': if not source_project: raise RuntimeError('Could not find project to run SCM inventory update from.') - self.sync_and_copy(source_project, private_data_dir) + self.sync_and_copy(source_project, private_data_dir, scm_branch=inventory_update.inventory_source.scm_branch) else: # If source is not SCM make an empty project directory, content is built inside inventory folder super(RunInventoryUpdate, self).build_project_dir(inventory_update, private_data_dir) @@ -1658,7 +1950,7 @@ def post_run_hook(self, inventory_update, status): raise PostRunError('Error occured while saving inventory data, see traceback or server logs', status='error', tb=traceback.format_exc()) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename) class RunAdHocCommand(BaseTask): """ Run an ad hoc command using ansible. @@ -1811,7 +2103,7 @@ def get_password_prompts(self, passwords={}): return d -@task(queue=get_local_queuename) +@task(queue=get_task_queuename) class RunSystemJob(BaseTask): model = SystemJob event_model = SystemJobEvent @@ -1831,6 +2123,8 @@ def build_args(self, system_job, private_data_dir, passwords): if system_job.job_type in ('cleanup_jobs', 'cleanup_activitystream'): if 'days' in json_vars: args.extend(['--days', str(json_vars.get('days', 60))]) + if 'batch_size' in json_vars: + args.extend(['--batch-size', str(json_vars['batch_size'])]) if 'dry_run' in json_vars and json_vars['dry_run']: args.extend(['--dry-run']) if system_job.job_type == 'cleanup_jobs': diff --git a/awx/main/tasks/policy.py b/awx/main/tasks/policy.py new file mode 100644 index 000000000000..9c92f62234b8 --- /dev/null +++ b/awx/main/tasks/policy.py @@ -0,0 +1,457 @@ +import json +import tempfile +import contextlib + +from pprint import pformat + +from typing import Optional, Union + +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from opa_client import OpaClient +from opa_client.base import BaseClient +from requests import HTTPError +from rest_framework import serializers +from rest_framework import fields + +from awx.main import models +from awx.main.exceptions import PolicyEvaluationError + +# Monkey patching opa_client.base.BaseClient to fix retries and timeout settings +_original_opa_base_client_init = BaseClient.__init__ + + +def _opa_base_client_init_fix( + self, + host: str = "localhost", + port: int = 8181, + version: str = "v1", + ssl: bool = False, + cert: Optional[Union[str, tuple]] = None, + headers: Optional[dict] = None, + retries: int = 2, + timeout: float = 1.5, +): + _original_opa_base_client_init(self, host, port, version, ssl, cert, headers) + self.retries = retries + self.timeout = timeout + + +BaseClient.__init__ = _opa_base_client_init_fix + + +class _TeamSerializer(serializers.ModelSerializer): + class Meta: + model = models.Team + fields = ('id', 'name') + + +class _UserSerializer(serializers.ModelSerializer): + teams = serializers.SerializerMethodField() + + class Meta: + model = models.User + fields = ('id', 'username', 'is_superuser', 'teams') + + def get_teams(self, user: models.User): + teams = models.Team.access_qs(user, 'member') + return _TeamSerializer(many=True).to_representation(teams) + + +class _ExecutionEnvironmentSerializer(serializers.ModelSerializer): + class Meta: + model = models.ExecutionEnvironment + fields = ( + 'id', + 'name', + 'image', + 'pull', + ) + + +class _InstanceGroupSerializer(serializers.ModelSerializer): + class Meta: + model = models.InstanceGroup + fields = ( + 'id', + 'name', + 'capacity', + 'jobs_running', + 'jobs_total', + 'max_concurrent_jobs', + 'max_forks', + ) + + +class _InventorySourceSerializer(serializers.ModelSerializer): + class Meta: + model = models.InventorySource + fields = ('id', 'name', 'source', 'status') + + +class _InventorySerializer(serializers.ModelSerializer): + inventory_sources = _InventorySourceSerializer(many=True) + + class Meta: + model = models.Inventory + fields = ( + 'id', + 'name', + 'description', + 'kind', + 'total_hosts', + 'total_groups', + 'has_inventory_sources', + 'total_inventory_sources', + 'has_active_failures', + 'hosts_with_active_failures', + 'inventory_sources', + ) + + +class _JobTemplateSerializer(serializers.ModelSerializer): + class Meta: + model = models.JobTemplate + fields = ( + 'id', + 'name', + 'job_type', + ) + + +class _WorkflowJobTemplateSerializer(serializers.ModelSerializer): + class Meta: + model = models.WorkflowJobTemplate + fields = ( + 'id', + 'name', + 'job_type', + ) + + +class _WorkflowJobSerializer(serializers.ModelSerializer): + class Meta: + model = models.WorkflowJob + fields = ( + 'id', + 'name', + ) + + +class _OrganizationSerializer(serializers.ModelSerializer): + class Meta: + model = models.Organization + fields = ( + 'id', + 'name', + ) + + +class _ProjectSerializer(serializers.ModelSerializer): + class Meta: + model = models.Project + fields = ( + 'id', + 'name', + 'status', + 'scm_type', + 'scm_url', + 'scm_branch', + 'scm_refspec', + 'scm_clean', + 'scm_track_submodules', + 'scm_delete_on_update', + ) + + +class _CredentialSerializer(serializers.ModelSerializer): + organization = _OrganizationSerializer() + + class Meta: + model = models.Credential + fields = ( + 'id', + 'name', + 'description', + 'organization', + 'credential_type', + 'managed', + 'kind', + 'cloud', + 'kubernetes', + ) + + +class _LabelSerializer(serializers.ModelSerializer): + organization = _OrganizationSerializer() + + class Meta: + model = models.Label + fields = ('id', 'name', 'organization') + + +class JobSerializer(serializers.ModelSerializer): + created_by = _UserSerializer() + credentials = _CredentialSerializer(many=True) + execution_environment = _ExecutionEnvironmentSerializer() + instance_group = _InstanceGroupSerializer() + inventory = _InventorySerializer() + job_template = _JobTemplateSerializer() + labels = _LabelSerializer(many=True) + organization = _OrganizationSerializer() + project = _ProjectSerializer() + extra_vars = fields.SerializerMethodField() + hosts_count = fields.SerializerMethodField() + workflow_job = fields.SerializerMethodField() + workflow_job_template = fields.SerializerMethodField() + + class Meta: + model = models.Job + fields = ( + 'id', + 'name', + 'created', + 'created_by', + 'credentials', + 'execution_environment', + 'extra_vars', + 'forks', + 'hosts_count', + 'instance_group', + 'inventory', + 'job_template', + 'job_type', + 'job_type_name', + 'labels', + 'launch_type', + 'limit', + 'launched_by', + 'organization', + 'playbook', + 'project', + 'scm_branch', + 'scm_revision', + 'workflow_job', + 'workflow_job_template', + ) + + def get_extra_vars(self, obj: models.Job): + return json.loads(obj.display_extra_vars()) + + def get_hosts_count(self, obj: models.Job): + return obj.hosts.count() + + def get_workflow_job(self, obj: models.Job): + workflow_job: models.WorkflowJob = obj.get_workflow_job() + if workflow_job is None: + return None + return _WorkflowJobSerializer().to_representation(workflow_job) + + def get_workflow_job_template(self, obj: models.Job): + workflow_job: models.WorkflowJob = obj.get_workflow_job() + if workflow_job is None: + return None + + workflow_job_template: models.WorkflowJobTemplate = workflow_job.workflow_job_template + if workflow_job_template is None: + return None + + return _WorkflowJobTemplateSerializer().to_representation(workflow_job_template) + + +class OPAResultSerializer(serializers.Serializer): + allowed = fields.BooleanField(required=True) + violations = fields.ListField(child=fields.CharField()) + + +class OPA_AUTH_TYPES: + NONE = 'None' + TOKEN = 'Token' + CERTIFICATE = 'Certificate' + + +@contextlib.contextmanager +def opa_cert_file(): + """ + Context manager that creates temporary certificate files for OPA authentication. + + For mTLS (mutual TLS), we need: + - Client certificate and key for client authentication + - CA certificate (optional) for server verification + + Returns: + tuple: (client_cert_path, verify_path) + - client_cert_path: Path to client cert file or None if not using client cert + - verify_path: Path to CA cert file, True to use system CA store, or False for no verification + """ + client_cert_temp = None + ca_temp = None + + try: + # Case 1: Full mTLS with client cert and optional CA cert + if settings.OPA_AUTH_TYPE == OPA_AUTH_TYPES.CERTIFICATE: + # Create client certificate file (required for mTLS) + client_cert_temp = tempfile.NamedTemporaryFile(delete=True, mode='w', suffix=".pem") + client_cert_temp.write(settings.OPA_AUTH_CLIENT_CERT) + client_cert_temp.write("\n") + client_cert_temp.write(settings.OPA_AUTH_CLIENT_KEY) + client_cert_temp.write("\n") + client_cert_temp.flush() + + # If CA cert is provided, use it for server verification + # Otherwise, use system CA store (True) + if settings.OPA_AUTH_CA_CERT: + ca_temp = tempfile.NamedTemporaryFile(delete=True, mode='w', suffix=".pem") + ca_temp.write(settings.OPA_AUTH_CA_CERT) + ca_temp.write("\n") + ca_temp.flush() + verify_path = ca_temp.name + else: + verify_path = True # Use system CA store + + yield (client_cert_temp.name, verify_path) + + # Case 2: TLS with only server verification (no client cert) + elif settings.OPA_SSL: + # If CA cert is provided, use it for server verification + # Otherwise, use system CA store (True) + if settings.OPA_AUTH_CA_CERT: + ca_temp = tempfile.NamedTemporaryFile(delete=True, mode='w', suffix=".pem") + ca_temp.write(settings.OPA_AUTH_CA_CERT) + ca_temp.write("\n") + ca_temp.flush() + verify_path = ca_temp.name + else: + verify_path = True # Use system CA store + + yield (None, verify_path) + + # Case 3: No TLS + else: + yield (None, False) + + finally: + # Clean up temporary files + if client_cert_temp: + client_cert_temp.close() + if ca_temp: + ca_temp.close() + + +@contextlib.contextmanager +def opa_client(headers=None): + with opa_cert_file() as cert_files: + cert, verify = cert_files + + with OpaClient( + host=settings.OPA_HOST, + port=settings.OPA_PORT, + headers=headers, + ssl=settings.OPA_SSL, + cert=cert, + timeout=settings.OPA_REQUEST_TIMEOUT, + retries=settings.OPA_REQUEST_RETRIES, + ) as client: + # Workaround for https://github.com/Turall/OPA-python-client/issues/32 + # by directly setting cert and verify on requests.session + client._session.cert = cert + client._session.verify = verify + + yield client + + +def evaluate_policy(instance): + # Policy evaluation for Policy as Code feature + if not settings.OPA_HOST: + return + + if not isinstance(instance, models.Job): + return + + instance.log_lifecycle("evaluate_policy") + + input_data = JobSerializer(instance=instance).data + + headers = settings.OPA_AUTH_CUSTOM_HEADERS + if settings.OPA_AUTH_TYPE == OPA_AUTH_TYPES.TOKEN: + headers.update({'Authorization': 'Bearer {}'.format(settings.OPA_AUTH_TOKEN)}) + + if settings.OPA_AUTH_TYPE == OPA_AUTH_TYPES.CERTIFICATE and not settings.OPA_SSL: + raise PolicyEvaluationError(_('OPA_AUTH_TYPE=Certificate requires OPA_SSL to be enabled.')) + + cert_settings_missing = [] + + if settings.OPA_AUTH_TYPE == OPA_AUTH_TYPES.CERTIFICATE: + if not settings.OPA_AUTH_CLIENT_CERT: + cert_settings_missing += ['OPA_AUTH_CLIENT_CERT'] + if not settings.OPA_AUTH_CLIENT_KEY: + cert_settings_missing += ['OPA_AUTH_CLIENT_KEY'] + if not settings.OPA_AUTH_CA_CERT: + cert_settings_missing += ['OPA_AUTH_CA_CERT'] + + if cert_settings_missing: + raise PolicyEvaluationError(_('Following certificate settings are missing for OPA_AUTH_TYPE=Certificate: {}').format(cert_settings_missing)) + + query_paths = [ + ('Organization', instance.organization.opa_query_path if instance.organization else None), + ('Inventory', instance.inventory.opa_query_path if instance.inventory else None), + ('Job template', instance.job_template.opa_query_path if instance.job_template else None), + ] + violations = dict() + errors = dict() + + try: + with opa_client(headers=headers) as client: + for path_type, query_path in query_paths: + response = dict() + try: + if not query_path: + continue + + response = client.query_rule(input_data=input_data, package_path=query_path) + + except HTTPError as e: + message = _('Call to OPA failed. Exception: {}').format(e) + try: + error_data = e.response.json() + except ValueError: + errors[path_type] = message + continue + + error_code = error_data.get("code") + error_message = error_data.get("message") + if error_code or error_message: + message = _('Call to OPA failed. Code: {}, Message: {}').format(error_code, error_message) + errors[path_type] = message + continue + + except Exception as e: + errors[path_type] = _('Call to OPA failed. Exception: {}').format(e) + continue + + result = response.get('result') + if result is None: + errors[path_type] = _('Call to OPA did not return a "result" property. The path refers to an undefined document.') + continue + + result_serializer = OPAResultSerializer(data=result) + if not result_serializer.is_valid(): + errors[path_type] = _('OPA policy returned invalid result.') + continue + + result_data = result_serializer.validated_data + if not result_data.get("allowed") and (result_violations := result_data.get("violations")): + violations[path_type] = result_violations + + format_results = dict() + if any(errors[e] for e in errors): + format_results["Errors"] = errors + + if any(violations[v] for v in violations): + format_results["Violations"] = violations + + if violations or errors: + raise PolicyEvaluationError(pformat(format_results, width=80)) + + except Exception as e: + raise PolicyEvaluationError(_('This job cannot be executed due to a policy violation or error. See the following details:\n{}').format(e)) diff --git a/awx/main/tasks/receptor.py b/awx/main/tasks/receptor.py index 006c805943c8..e1ccf4d7c440 100644 --- a/awx/main/tasks/receptor.py +++ b/awx/main/tasks/receptor.py @@ -17,6 +17,12 @@ # Runner import ansible_runner +# django-ansible-base +from ansible_base.lib.utils.db import advisory_lock + +# Dispatcherd +from dispatcherd.publish import task + # AWX from awx.main.utils.execution_environments import get_default_pod_spec from awx.main.exceptions import ReceptorNodeNotFound @@ -27,9 +33,8 @@ ) from awx.main.constants import MAX_ISOLATED_PATH_COLON_DELIMITER from awx.main.tasks.signals import signal_state, signal_callback, SignalExit -from awx.main.models import Instance, InstanceLink, UnifiedJob -from awx.main.dispatch import get_local_queuename -from awx.main.dispatch.publish import task +from awx.main.models import Instance, InstanceLink, UnifiedJob, ReceptorAddress +from awx.main.dispatch import get_task_queuename # Receptorctl from receptorctl.socket_interface import ReceptorControl @@ -48,6 +53,70 @@ class ReceptorConnectionType(Enum): STREAMTLS = 2 +""" +Translate receptorctl messages that come in over stdout into +structured messages. Currently, these are error messages. +""" + + +class ReceptorErrorBase: + _MESSAGE = 'Receptor Error' + + def __init__(self, node: str = 'N/A', state_name: str = 'N/A'): + self.node = node + self.state_name = state_name + + def __str__(self): + return f"{self.__class__.__name__} '{self._MESSAGE}' on node '{self.node}' with state '{self.state_name}'" + + +class WorkUnitError(ReceptorErrorBase): + _MESSAGE = 'unknown work unit ' + + def __init__(self, work_unit_id: str, *args, **kwargs): + super().__init__(*args, **kwargs) + self.work_unit_id = work_unit_id + + def __str__(self): + return f"{super().__str__()} work unit id '{self.work_unit_id}'" + + +class WorkUnitCancelError(WorkUnitError): + _MESSAGE = 'error cancelling remote unit: unknown work unit ' + + +class WorkUnitResultsError(WorkUnitError): + _MESSAGE = 'Failed to get results: unknown work unit ' + + +class UnknownError(ReceptorErrorBase): + _MESSAGE = 'Unknown receptor ctl error' + + def __init__(self, msg, *args, **kwargs): + super().__init__(*args, **kwargs) + self._MESSAGE = msg + + +class FuzzyError: + def __new__(self, e: RuntimeError, node: str, state_name: str): + """ + At the time of writing this comment all of the sub-classes detection + is centralized in this parent class. It's like a Router(). + Someone may find it better to push down the error detection logic into + each sub-class. + """ + msg = e.args[0] + + common_startswith = (WorkUnitCancelError, WorkUnitResultsError, WorkUnitError) + + for klass in common_startswith: + if msg.startswith(klass._MESSAGE): + work_unit_id = msg[len(klass._MESSAGE) :] + return klass(work_unit_id, node=node, state_name=state_name) + + return UnknownError(msg, node=node, state_name=state_name) + + def read_receptor_config(): # for K8S deployments, getting a lock is necessary as another process # may be re-writing the config at this time @@ -161,22 +230,24 @@ class RemoteJobError(RuntimeError): pass -def run_until_complete(node, timing_data=None, **kwargs): +def run_until_complete(node, timing_data=None, worktype='ansible-runner', ttl='20s', **kwargs): """ Runs an ansible-runner work_type on remote node, waits until it completes, then returns stdout. """ + config_data = read_receptor_config() receptor_ctl = get_receptor_ctl(config_data) use_stream_tls = getattr(get_conn_type(node, receptor_ctl), 'name', None) == "STREAMTLS" kwargs.setdefault('tlsclient', get_tls_client(config_data, use_stream_tls)) - kwargs.setdefault('ttl', '20s') + if ttl is not None: + kwargs['ttl'] = ttl kwargs.setdefault('payload', '') if work_signing_enabled(config_data): kwargs['signwork'] = True transmit_start = time.time() - result = receptor_ctl.submit_work(worktype='ansible-runner', node=node, **kwargs) + result = receptor_ctl.submit_work(worktype=worktype, node=node, **kwargs) unit_id = result['unitid'] run_start = time.time() @@ -184,6 +255,7 @@ def run_until_complete(node, timing_data=None, **kwargs): timing_data['transmit_timing'] = run_start - transmit_start run_timing = 0.0 stdout = '' + state_name = 'local var never set' try: resultfile = receptor_ctl.get_work_results(unit_id) @@ -204,13 +276,33 @@ def run_until_complete(node, timing_data=None, **kwargs): stdout = resultfile.read() stdout = str(stdout, encoding='utf-8') + except RuntimeError as e: + receptor_e = FuzzyError(e, node, state_name) + if type(receptor_e) in ( + WorkUnitError, + WorkUnitResultsError, + ): + logger.warning(f'While consuming job results: {receptor_e}') + else: + raise finally: if settings.RECEPTOR_RELEASE_WORK: - res = receptor_ctl.simple_command(f"work release {unit_id}") - if res != {'released': unit_id}: - logger.warning(f'Could not confirm release of receptor work unit id {unit_id} from {node}, data: {res}') - - receptor_ctl.close() + try: + res = receptor_ctl.simple_command(f"work release {unit_id}") + + if res != {'released': unit_id}: + logger.warning(f'Could not confirm release of receptor work unit id {unit_id} from {node}, data: {res}') + + receptor_ctl.close() + except RuntimeError as e: + receptor_e = FuzzyError(e, node, state_name) + if type(receptor_e) in ( + WorkUnitError, + WorkUnitCancelError, + ): + logger.warning(f"While releasing work: {receptor_e}") + else: + logger.error(f"While releasing work: {receptor_e}") if state_name.lower() == 'failed': work_detail = status.get('Detail', '') @@ -274,7 +366,7 @@ def _convert_args_to_cli(vargs): args = ['cleanup'] for option in ('exclude_strings', 'remove_images'): if vargs.get(option): - args.append('--{}={}'.format(option.replace('_', '-'), ' '.join(vargs.get(option)))) + args.append('--{} {}'.format(option.replace('_', '-'), ' '.join(f'"{item}"' for item in vargs.get(option)))) for option in ('file_pattern', 'image_prune', 'process_isolation_executable', 'grace_period'): if vargs.get(option) is True: args.append('--{}'.format(option.replace('_', '-'))) @@ -283,7 +375,7 @@ def _convert_args_to_cli(vargs): return args -def worker_cleanup(node_name, vargs, timeout=300.0): +def worker_cleanup(node_name, vargs): args = _convert_args_to_cli(vargs) remote_command = ' '.join(args) @@ -317,12 +409,40 @@ def run(self): res = self._run_internal(receptor_ctl) return res finally: - # Make sure to always release the work unit if we established it - if self.unit_id is not None and settings.RECEPTOR_RELEASE_WORK: - try: - receptor_ctl.simple_command(f"work release {self.unit_id}") - except Exception: - logger.exception(f"Error releasing work unit {self.unit_id}.") + status = getattr(res, 'status', 'error') + self._receptor_release_work(receptor_ctl, status) + + def _receptor_release_work(self, receptor_ctl: ReceptorControl, status: str) -> None: + """ + Releases the work unit from Receptor if certain conditions are met. + This method checks several conditions before attempting to release the work unit: + - If `self.unit_id` is `None`, the method returns immediately. + - If the `RECEPTOR_RELEASE_WORK` setting is `False`, the method returns immediately. + - If the `RECEPTOR_KEEP_WORK_ON_ERROR` setting is `True` and the status is 'error', the method returns immediately. + If none of the above conditions are met, the method attempts to release the work unit using the Receptor control command. + If an exception occurs during the release process, it logs an error message. + Args: + receptor_ctl (ReceptorControl): The Receptor control object used to issue commands. + status (str): The status of the work unit, which may affect whether it is released. + """ + + if self.unit_id is None: + logger.debug("No work unit ID to release.") + return + + if settings.RECEPTOR_RELEASE_WORK is False: + logger.debug(f"RECEPTOR_RELEASE_WORK is False, not releasing work unit {self.unit_id}.") + return + + if settings.RECEPTOR_KEEP_WORK_ON_ERROR and status == 'error': + logger.debug(f"RECEPTOR_KEEP_WORK_ON_ERROR is True and status is 'error', not releasing work unit {self.unit_id}.") + return + + try: + logger.debug(f"Released work unit {self.unit_id}.") + receptor_ctl.simple_command(f"work release {self.unit_id}") + except Exception: + logger.exception(f"Error releasing work unit {self.unit_id}.") def _run_internal(self, receptor_ctl): # Create a socketpair. Where the left side will be used for writing our payload @@ -431,16 +551,16 @@ def _run_internal(self, receptor_ctl): # massive, only ask for last 1000 bytes startpos = max(stdout_size - 1000, 0) resultsock, resultfile = receptor_ctl.get_work_results(self.unit_id, startpos=startpos, return_socket=True, return_sockfile=True) - resultsock.setblocking(False) # this makes resultfile reads non blocking lines = resultfile.readlines() receptor_output = b"".join(lines).decode() if receptor_output: - self.task.runner_callback.delay_update(result_traceback=receptor_output) + self.task.runner_callback.delay_update(result_traceback=f'Worker output:\n{receptor_output}') elif detail: - self.task.runner_callback.delay_update(result_traceback=detail) + self.task.runner_callback.delay_update(result_traceback=f'Receptor detail:\n{detail}') else: logger.warning(f'No result details or output from {self.task.instance.log_format}, status:\n{state_name}') except Exception: + logger.exception(f'Work results error from job id={self.task.instance.id} work_unit={self.task.instance.work_unit_id}') raise RuntimeError(detail) return res @@ -464,6 +584,7 @@ def processor(self, resultfile): event_handler=self.task.runner_callback.event_handler, finished_callback=self.task.runner_callback.finished_callback, status_handler=self.task.runner_callback.status_handler, + artifacts_handler=self.task.runner_callback.artifacts_handler, **self.runner_params, ) @@ -526,6 +647,10 @@ def pod_definition(self): pod_spec['spec']['containers'][0]['image'] = ee.image pod_spec['spec']['containers'][0]['args'] = ['ansible-runner', 'worker', '--private-data-dir=/runner'] + if settings.AWX_RUNNER_KEEPALIVE_SECONDS: + pod_spec['spec']['containers'][0].setdefault('env', []) + pod_spec['spec']['containers'][0]['env'].append({'name': 'ANSIBLE_RUNNER_KEEPALIVE_SECONDS', 'value': str(settings.AWX_RUNNER_KEEPALIVE_SECONDS)}) + # Enforce EE Pull Policy pull_options = {"always": "Always", "missing": "IfNotPresent", "never": "Never"} if self.task and self.task.instance.execution_environment: @@ -635,11 +760,11 @@ def kube_config(self): # RECEPTOR_CONFIG_STARTER = ( {'local-only': None}, - {'log-level': 'debug'}, + {'log-level': settings.RECEPTOR_LOG_LEVEL}, {'node': {'firewallrules': [{'action': 'reject', 'tonode': settings.CLUSTER_HOST_ID, 'toservice': 'control'}]}}, {'control-service': {'service': 'control', 'filename': '/var/run/receptor/receptor.sock', 'permissions': '0660'}}, {'work-command': {'worktype': 'local', 'command': 'ansible-runner', 'params': 'worker', 'allowruntimeparams': True}}, - {'work-signing': {'privatekey': '/etc/receptor/signing/work-private-key.pem', 'tokenexpiration': '1m'}}, + {'work-signing': {'privatekey': '/etc/receptor/work_private_key.pem', 'tokenexpiration': '1m'}}, { 'work-kubernetes': { 'worktype': 'kubernetes-runtime-auth', @@ -661,34 +786,58 @@ def kube_config(self): { 'tls-client': { 'name': 'tlsclient', - 'rootcas': '/etc/receptor/tls/ca/receptor-ca.crt', + 'rootcas': '/etc/receptor/tls/ca/mesh-CA.crt', 'cert': '/etc/receptor/tls/receptor.crt', 'key': '/etc/receptor/tls/receptor.key', + 'mintls13': False, } }, ) -@task() -def write_receptor_config(): - lock = FileLock(__RECEPTOR_CONF_LOCKFILE) - with lock: - receptor_config = list(RECEPTOR_CONFIG_STARTER) - - this_inst = Instance.objects.me() - instances = Instance.objects.filter(node_type=Instance.Types.EXECUTION) - existing_peers = {link.target_id for link in InstanceLink.objects.filter(source=this_inst)} - new_links = [] - for instance in instances: - peer = {'tcp-peer': {'address': f'{instance.hostname}:{instance.listener_port}', 'tls': 'tlsclient'}} +def should_update_config(new_config): + ''' + checks that the list of instances matches the list of + tcp-peers in the config + ''' + + current_config = read_receptor_config() # this gets receptor conf lock + for config_entry in current_config: + if config_entry not in new_config: + logger.warning(f"{config_entry} should not be in receptor config. Updating.") + return True + for config_entry in new_config: + if config_entry not in current_config: + logger.warning(f"{config_entry} missing from receptor config. Updating.") + return True + + return False + + +def generate_config_data(): + # returns two values + # receptor config - based on current database peers + # should_update - If True, receptor_config differs from the receptor conf file on disk + addresses = ReceptorAddress.objects.filter(peers_from_control_nodes=True) + + receptor_config = list(RECEPTOR_CONFIG_STARTER) + for address in addresses: + if address.get_peer_type(): + peer = { + f'{address.get_peer_type()}': { + 'address': f'{address.get_full_address()}', + 'tls': 'tlsclient', + } + } receptor_config.append(peer) - if instance.id not in existing_peers: - new_links.append(InstanceLink(source=this_inst, target=instance, link_state=InstanceLink.States.ADDING)) + else: + logger.warning(f"Receptor address {address} has unsupported peer type, skipping.") + should_update = should_update_config(receptor_config) + return receptor_config, should_update - InstanceLink.objects.bulk_create(new_links) - with open(__RECEPTOR_CONF, 'w') as file: - yaml.dump(receptor_config, file, default_flow_style=False) +def reload_receptor(): + logger.warning("Receptor config changed, reloading receptor") # This needs to be outside of the lock because this function itself will acquire the lock. receptor_ctl = get_receptor_ctl() @@ -704,14 +853,34 @@ def write_receptor_config(): else: raise RuntimeError("Receptor reload failed") - links = InstanceLink.objects.filter(source=this_inst, target__in=instances, link_state=InstanceLink.States.ADDING) - links.update(link_state=InstanceLink.States.ESTABLISHED) - -@task(queue=get_local_queuename) +@task(on_duplicate='queue_one') +def write_receptor_config(): + """ + This task runs async on each control node, K8S only. + It is triggered whenever remote is added or removed, or if peers_from_control_nodes + is flipped. + It is possible for write_receptor_config to be called multiple times. + For example, if new instances are added in quick succession. + To prevent that case, each control node first grabs a DB advisory lock, specific + to just that control node (i.e. multiple control nodes can run this function + at the same time, since it only writes the local receptor config file) + """ + with advisory_lock(f"{settings.CLUSTER_HOST_ID}_write_receptor_config", wait=True): + # Config file needs to be updated + receptor_config, should_update = generate_config_data() + if should_update: + lock = FileLock(__RECEPTOR_CONF_LOCKFILE) + with lock: + with open(__RECEPTOR_CONF, 'w') as file: + yaml.dump(receptor_config, file, default_flow_style=False) + reload_receptor() + + +@task(queue=get_task_queuename, on_duplicate='discard') def remove_deprovisioned_node(hostname): InstanceLink.objects.filter(source__hostname=hostname).update(link_state=InstanceLink.States.REMOVING) - InstanceLink.objects.filter(target__hostname=hostname).update(link_state=InstanceLink.States.REMOVING) + InstanceLink.objects.filter(target__instance__hostname=hostname).update(link_state=InstanceLink.States.REMOVING) node_jobs = UnifiedJob.objects.filter( execution_node=hostname, @@ -725,6 +894,3 @@ def remove_deprovisioned_node(hostname): # This will as a side effect also delete the InstanceLinks that are tied to it. Instance.objects.filter(hostname=hostname).delete() - - # Update the receptor configs for all of the control-plane. - write_receptor_config.apply_async(queue='tower_broadcast_all') diff --git a/awx/main/tasks/signals.py b/awx/main/tasks/signals.py index 95610548b991..a1607bfc9960 100644 --- a/awx/main/tasks/signals.py +++ b/awx/main/tasks/signals.py @@ -2,7 +2,6 @@ import functools import logging - logger = logging.getLogger('awx.main.tasks.signals') @@ -14,33 +13,50 @@ class SignalExit(Exception): class SignalState: + # SIGTERM: Sent by supervisord to process group on shutdown + # SIGUSR1: The dispatcherd cancel signal + signals = (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1) + def reset(self): - self.sigterm_flag = False - self.is_active = False - self.original_sigterm = None - self.original_sigint = None + for for_signal in self.signals: + self.signal_flags[for_signal] = False + self.original_methods[for_signal] = None + + self.is_active = False # for nested context managers self.raise_exception = False def __init__(self): + self.signal_flags = {} + self.original_methods = {} self.reset() - def set_flag(self, *args): - """Method to pass into the python signal.signal method to receive signals""" - self.sigterm_flag = True + def raise_if_needed(self): if self.raise_exception: self.raise_exception = False # so it is not raised a second time in error handling raise SignalExit() + def set_signal_flag(self, *args, for_signal=None): + self.signal_flags[for_signal] = True + logger.info(f'Processed signal {for_signal}, set exit flag') + self.raise_if_needed() + def connect_signals(self): - self.original_sigterm = signal.getsignal(signal.SIGTERM) - self.original_sigint = signal.getsignal(signal.SIGINT) - signal.signal(signal.SIGTERM, self.set_flag) - signal.signal(signal.SIGINT, self.set_flag) + for for_signal in self.signals: + self.original_methods[for_signal] = signal.getsignal(for_signal) + signal.signal(for_signal, lambda *args, for_signal=for_signal: self.set_signal_flag(*args, for_signal=for_signal)) self.is_active = True def restore_signals(self): - signal.signal(signal.SIGTERM, self.original_sigterm) - signal.signal(signal.SIGINT, self.original_sigint) + for for_signal in self.signals: + original_method = self.original_methods[for_signal] + signal.signal(for_signal, original_method) + # if we got a signal while context manager was active, call parent methods. + if self.signal_flags[for_signal]: + if callable(original_method): + try: + original_method() + except Exception as exc: + logger.info(f'Error processing original {for_signal} signal, error: {str(exc)}') self.reset() @@ -48,12 +64,12 @@ def restore_signals(self): def signal_callback(): - return signal_state.sigterm_flag + return any(signal_state.signal_flags[for_signal] for for_signal in signal_state.signals) def with_signal_handling(f): """ - Change signal handling to make signal_callback return True in event of SIGTERM or SIGINT. + Change signal handling to make signal_callback return True in event of SIGTERM, SIGINT, or SIGUSR1. """ @functools.wraps(f) diff --git a/awx/main/tasks/system.py b/awx/main/tasks/system.py index 482c168af210..fda46b296849 100644 --- a/awx/main/tasks/system.py +++ b/awx/main/tasks/system.py @@ -1,74 +1,78 @@ # Python -from collections import namedtuple import functools import importlib +import itertools import json import logging import os -from io import StringIO -from contextlib import redirect_stdout import shutil import time -from distutils.version import LooseVersion as Version -from datetime import datetime +from collections import namedtuple +from contextlib import redirect_stdout +from packaging.version import Version +from io import StringIO + +# dispatcherd +from dispatcherd.factories import get_control_from_settings +from dispatcherd.publish import task + +# Runner +import ansible_runner.cleanup +import psycopg +from ansible_base.lib.utils.db import advisory_lock + +# django-ansible-base +from ansible_base.resource_registry.tasks.sync import SyncExecutor + +# Django-CRUM +from crum import impersonate + +# dateutil +from dateutil.parser import parse as parse_date # Django from django.conf import settings -from django.db import transaction, DatabaseError, IntegrityError +from django.contrib.auth.models import User +from django.core.cache import cache +from django.core.exceptions import ObjectDoesNotExist +from django.db import DatabaseError, IntegrityError, connection, transaction from django.db.models.fields.related import ForeignKey -from django.utils.timezone import now, timedelta +from django.db.models.query import QuerySet from django.utils.encoding import smart_str -from django.contrib.auth.models import User +from django.utils.timezone import now, timedelta from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_noop -from django.core.cache import cache -from django.core.exceptions import ObjectDoesNotExist - -# Django-CRUM -from crum import impersonate - -# Runner -import ansible_runner.cleanup - -# dateutil -from dateutil.parser import parse as parse_date +# Django flags +from flags.state import flag_enabled +from rest_framework.exceptions import PermissionDenied # AWX from awx import __version__ as awx_application_version +from awx.conf import settings_registry +from awx.main import analytics from awx.main.access import access_registry +from awx.main.analytics.subsystem_metrics import DispatcherMetrics +from awx.main.constants import ACTIVE_STATES, ERROR_STATES +from awx.main.consumers import emit_channel_notification +from awx.main.dispatch import get_task_queuename, reaper from awx.main.models import ( - Schedule, - TowerScheduleState, Instance, InstanceGroup, - UnifiedJob, - Notification, Inventory, - SmartInventoryMembership, Job, + Notification, + Schedule, + SmartInventoryMembership, + TowerScheduleState, + UnifiedJob, + convert_jsonfields, ) -from awx.main.constants import ACTIVE_STATES -from awx.main.dispatch.publish import task -from awx.main.dispatch import get_local_queuename, reaper -from awx.main.utils.common import ( - get_type_for_model, - ignore_inventory_computed_fields, - ignore_inventory_group_removal, - ScheduleWorkflowManager, - ScheduleTaskManager, -) - -from awx.main.utils.external_logging import reconfigure_rsyslog +from awx.main.tasks.helpers import is_run_threshold_reached +from awx.main.tasks.host_indirect import save_indirect_host_entries +from awx.main.tasks.receptor import administrative_workunit_reaper, get_receptor_ctl, worker_cleanup, worker_info, write_receptor_config +from awx.main.utils.common import ignore_inventory_computed_fields, ignore_inventory_group_removal from awx.main.utils.reload import stop_local_services -from awx.main.utils.pglock import advisory_lock -from awx.main.tasks.receptor import get_receptor_ctl, worker_info, worker_cleanup, administrative_workunit_reaper, write_receptor_config -from awx.main.consumers import emit_channel_notification -from awx.main import analytics -from awx.conf import settings_registry -from awx.main.analytics.subsystem_metrics import Metrics - -from rest_framework.exceptions import PermissionDenied logger = logging.getLogger('awx.main.tasks.system') @@ -79,19 +83,32 @@ ''' -def dispatch_startup(): +def _run_dispatch_startup_common(): + """ + Execute the common startup initialization steps. + This includes updating schedules, syncing instance membership, and starting + local reaping and resetting metrics. + """ startup_logger = logging.getLogger('awx.main.tasks') # TODO: Enable this on VM installs if settings.IS_K8S: - write_receptor_config() + try: + write_receptor_config() + except Exception: + logger.exception("Failed to write receptor config, skipping.") + + try: + convert_jsonfields() + except Exception: + logger.exception("Failed JSON field conversion, skipping.") - startup_logger.debug("Syncing Schedules") + startup_logger.debug("Syncing schedules") for sch in Schedule.objects.all(): try: sch.update_computed_fields() except Exception: - logger.exception("Failed to rebuild schedule {}.".format(sch)) + logger.exception("Failed to rebuild schedule %s.", sch) # # When the dispatcher starts, if the instance cannot be found in the database, @@ -109,30 +126,95 @@ def dispatch_startup(): # no-op. # apply_cluster_membership_policies() - cluster_node_heartbeat() + cluster_node_heartbeat(None) reaper.startup_reaping() - reaper.reap_waiting(grace_period=0) - m = Metrics() + m = DispatcherMetrics() m.reset_values() - # Update Tower's rsyslog.conf file based on loggins settings in the db - reconfigure_rsyslog() + +def _dispatcherd_dispatch_startup(): + """ + New dispatcherd branch for startup: uses the control API to re-submit waiting jobs. + """ + logger.debug("Dispatcherd enabled: dispatching waiting jobs via control channel") + from awx.main.tasks.jobs import dispatch_waiting_jobs + + dispatch_waiting_jobs.apply_async(queue=get_task_queuename()) + + +def dispatch_startup(): + """ + System initialization at startup. + First, execute the common logic. + Then, re-submit waiting jobs via the control API. + """ + _run_dispatch_startup_common() + _dispatcherd_dispatch_startup() def inform_cluster_of_shutdown(): + """ + Clean system shutdown that marks the current instance offline. + Relies on dispatcherd's built-in cleanup. + """ try: - this_inst = Instance.objects.get(hostname=settings.CLUSTER_HOST_ID) - this_inst.mark_offline(update_last_seen=True, errors=_('Instance received normal shutdown signal')) - try: - reaper.reap_waiting(this_inst, grace_period=0) - except Exception: - logger.exception('failed to reap waiting jobs for {}'.format(this_inst.hostname)) - logger.warning('Normal shutdown signal for instance {}, removed self from capacity pool.'.format(this_inst.hostname)) - except Exception: - logger.exception('Encountered problem with normal shutdown signal.') + inst = Instance.objects.get(hostname=settings.CLUSTER_HOST_ID) + inst.mark_offline(update_last_seen=True, errors=_('Instance received normal shutdown signal')) + except Instance.DoesNotExist: + logger.exception("Cluster host not found: %s", settings.CLUSTER_HOST_ID) + return + + logger.debug("No extra reaping required for instance %s", inst.hostname) + logger.warning("Normal shutdown processed for instance %s; instance removed from capacity pool.", inst.hostname) + + +@task(queue=get_task_queuename, timeout=3600 * 5) +def migrate_jsonfield(table, pkfield, columns): + batchsize = 10000 + with advisory_lock(f'json_migration_{table}', wait=False) as acquired: + if not acquired: + return + + from django.db.migrations.executor import MigrationExecutor + + # If Django is currently running migrations, wait until it is done. + while True: + executor = MigrationExecutor(connection) + if not executor.migration_plan(executor.loader.graph.leaf_nodes()): + break + time.sleep(120) + + logger.warning(f"Migrating json fields for {table}: {', '.join(columns)}") + + with connection.cursor() as cursor: + for i in itertools.count(0, batchsize): + # Are there even any rows in the table beyond this point? + cursor.execute(f"select count(1) from {table} where {pkfield} >= %s limit 1;", (i,)) + if not cursor.fetchone()[0]: + break + + column_expr = ', '.join(f"{colname} = {colname}_old::jsonb" for colname in columns) + # If any of the old columns have non-null values, the data needs to be cast and copied over. + empty_expr = ' or '.join(f"{colname}_old is not null" for colname in columns) + cursor.execute( # Only clobber the new fields if there is non-null data in the old ones. + f""" + update {table} + set {column_expr} + where {pkfield} >= %s and {pkfield} < %s + and {empty_expr}; + """, + (i, i + batchsize), + ) + rows = cursor.rowcount + logger.debug(f"Batch {i} to {i + batchsize} copied on {table}, {rows} rows affected.") + column_expr = ', '.join(f"DROP COLUMN {column}_old" for column in columns) + cursor.execute(f"ALTER TABLE {table} {column_expr};") -@task(queue=get_local_queuename) + logger.warning(f"Migration of {table} to jsonb is finished.") + + +@task(queue=get_task_queuename, timeout=3600, on_duplicate='queue_one') def apply_cluster_membership_policies(): from awx.main.signals import disable_activity_stream @@ -244,8 +326,10 @@ def apply_cluster_membership_policies(): logger.debug('Cluster policy computation finished in {} seconds'.format(time.time() - started_compute)) -@task(queue='tower_broadcast_all') -def handle_setting_changes(setting_keys): +@task(queue='tower_settings_change', timeout=600) +def clear_setting_cache(setting_keys): + # log that cache is being cleared + logger.info(f"clear_setting_cache of keys {setting_keys}") orig_len = len(setting_keys) for i in range(orig_len): for dependent_key in settings_registry.get_dependent_settings(setting_keys[i]): @@ -254,11 +338,13 @@ def handle_setting_changes(setting_keys): logger.debug('cache delete_many(%r)', cache_keys) cache.delete_many(cache_keys) - if any([setting.startswith('LOG_AGGREGATOR') for setting in setting_keys]): - reconfigure_rsyslog() + if 'LOG_AGGREGATOR_LEVEL' in setting_keys: + ctl = get_control_from_settings() + ctl.queuename = get_task_queuename() + ctl.control('set_log_level', data={'level': settings.LOG_AGGREGATOR_LEVEL}) -@task(queue='tower_broadcast_all') +@task(queue='tower_broadcast_all', timeout=600) def delete_project_files(project_path): # TODO: possibly implement some retry logic lock_file = project_path + '.lock' @@ -286,7 +372,7 @@ def profile_sql(threshold=1, minutes=1): logger.error('SQL QUERIES >={}s ENABLED FOR {} MINUTE(S)'.format(threshold, minutes)) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=1800) def send_notifications(notification_list, job_id=None): if not isinstance(notification_list, list): raise TypeError("notification_list should be of type list") @@ -317,20 +403,27 @@ def send_notifications(notification_list, job_id=None): logger.exception('Error saving notification {} result.'.format(notification.id)) -@task(queue=get_local_queuename) -def gather_analytics(): - from awx.conf.models import Setting - from rest_framework.fields import DateTimeField +def events_processed_hook(unified_job): + """This method is intended to be called for every unified job + after the playbook_on_stats/EOF event is processed and final status is saved + Either one of these events could happen before the other, or there may be no events""" + unified_job.send_notification_templates('succeeded' if unified_job.status == 'successful' else 'failed') + if isinstance(unified_job, Job) and flag_enabled("FEATURE_INDIRECT_NODE_COUNTING_ENABLED"): + if unified_job.event_queries_processed is True: + # If this is called from callback receiver, it likely does not have updated model data + # a refresh now is formally robust + unified_job.refresh_from_db(fields=['event_queries_processed']) + if unified_job.event_queries_processed is False: + save_indirect_host_entries.delay(unified_job.id) - last_gather = Setting.objects.filter(key='AUTOMATION_ANALYTICS_LAST_GATHER').first() - last_time = DateTimeField().to_internal_value(last_gather.value) if last_gather and last_gather.value else None - gather_time = now() - if not last_time or ((gather_time - last_time).total_seconds() > settings.AUTOMATION_ANALYTICS_GATHER_INTERVAL): +@task(queue=get_task_queuename, timeout=3600 * 5, on_duplicate='discard') +def gather_analytics(): + if is_run_threshold_reached(getattr(settings, 'AUTOMATION_ANALYTICS_LAST_GATHER', None), settings.AUTOMATION_ANALYTICS_GATHER_INTERVAL): analytics.gather() -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=600, on_duplicate='queue_one') def purge_old_stdout_files(): nowtime = time.time() for f in os.listdir(settings.JOBOUTPUT_ROOT): @@ -339,70 +432,71 @@ def purge_old_stdout_files(): logger.debug("Removing {}".format(os.path.join(settings.JOBOUTPUT_ROOT, f))) -def _cleanup_images_and_files(**kwargs): - if settings.IS_K8S: - return - this_inst = Instance.objects.me() - runner_cleanup_kwargs = this_inst.get_cleanup_task_kwargs(**kwargs) - if runner_cleanup_kwargs: - stdout = '' - with StringIO() as buffer: - with redirect_stdout(buffer): - ansible_runner.cleanup.run_cleanup(runner_cleanup_kwargs) - stdout = buffer.getvalue() - if '(changed: True)' in stdout: - logger.info(f'Performed local cleanup with kwargs {kwargs}, output:\n{stdout}') - - # if we are the first instance alphabetically, then run cleanup on execution nodes - checker_instance = ( - Instance.objects.filter(node_type__in=['hybrid', 'control'], node_state=Instance.States.READY, enabled=True, capacity__gt=0) - .order_by('-hostname') - .first() - ) - if checker_instance and this_inst.hostname == checker_instance.hostname: - for inst in Instance.objects.filter(node_type='execution', node_state=Instance.States.READY, enabled=True, capacity__gt=0): - runner_cleanup_kwargs = inst.get_cleanup_task_kwargs(**kwargs) - if not runner_cleanup_kwargs: - continue - try: - stdout = worker_cleanup(inst.hostname, runner_cleanup_kwargs) - if '(changed: True)' in stdout: - logger.info(f'Performed cleanup on execution node {inst.hostname} with output:\n{stdout}') - except RuntimeError: - logger.exception(f'Error running cleanup on execution node {inst.hostname}') +class CleanupImagesAndFiles: + @classmethod + def get_first_control_instance(cls) -> Instance | None: + return ( + Instance.objects.filter(node_type__in=['hybrid', 'control'], node_state=Instance.States.READY, enabled=True, capacity__gt=0) + .order_by('-hostname') + .first() + ) + @classmethod + def get_execution_instances(cls) -> QuerySet[Instance]: + return Instance.objects.filter(node_type='execution', node_state=Instance.States.READY, enabled=True, capacity__gt=0) -@task(queue='tower_broadcast_all') + @classmethod + def run_local(cls, this_inst: Instance, **kwargs): + if settings.IS_K8S: + return + runner_cleanup_kwargs = this_inst.get_cleanup_task_kwargs(**kwargs) + if runner_cleanup_kwargs: + stdout = '' + with StringIO() as buffer: + with redirect_stdout(buffer): + ansible_runner.cleanup.run_cleanup(runner_cleanup_kwargs) + stdout = buffer.getvalue() + if '(changed: True)' in stdout: + logger.info(f'Performed local cleanup with kwargs {kwargs}, output:\n{stdout}') + + @classmethod + def run_remote(cls, this_inst: Instance, **kwargs): + # if we are the first instance alphabetically, then run cleanup on execution nodes + checker_instance = cls.get_first_control_instance() + + if checker_instance and this_inst.hostname == checker_instance.hostname: + for inst in cls.get_execution_instances(): + runner_cleanup_kwargs = inst.get_cleanup_task_kwargs(**kwargs) + if not runner_cleanup_kwargs: + continue + try: + stdout = worker_cleanup(inst.hostname, runner_cleanup_kwargs) + if '(changed: True)' in stdout: + logger.info(f'Performed cleanup on execution node {inst.hostname} with output:\n{stdout}') + except RuntimeError: + logger.exception(f'Error running cleanup on execution node {inst.hostname}') + + @classmethod + def run(cls, **kwargs): + if settings.IS_K8S: + return + this_inst = Instance.objects.me() + cls.run_local(this_inst, **kwargs) + cls.run_remote(this_inst, **kwargs) + + +@task(queue='tower_broadcast_all', timeout=3600) def handle_removed_image(remove_images=None): """Special broadcast invocation of this method to handle case of deleted EE""" - _cleanup_images_and_files(remove_images=remove_images, file_pattern='') + CleanupImagesAndFiles.run(remove_images=remove_images, file_pattern='') -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=3600, on_duplicate='queue_one') def cleanup_images_and_files(): - _cleanup_images_and_files() - - -@task(queue=get_local_queuename) -def cluster_node_health_check(node): - """ - Used for the health check endpoint, refreshes the status of the instance, but must be ran on target node - """ - if node == '': - logger.warning('Local health check incorrectly called with blank string') - return - elif node != settings.CLUSTER_HOST_ID: - logger.warning(f'Local health check for {node} incorrectly sent to {settings.CLUSTER_HOST_ID}') - return - try: - this_inst = Instance.objects.me() - except Instance.DoesNotExist: - logger.warning(f'Instance record for {node} missing, could not check capacity.') - return - this_inst.local_health_check() + CleanupImagesAndFiles.run(image_prune=True) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=600, on_duplicate='queue_one') def execution_node_health_check(node): if node == '': logger.warning('Remote health check incorrectly called with blank string') @@ -424,7 +518,6 @@ def execution_node_health_check(node): data = worker_info(node) prior_capacity = instance.capacity - instance.save_health_data( version='ansible-runner-' + data.get('runner_version', '???'), cpu=data.get('cpu_count', 0), @@ -445,12 +538,44 @@ def execution_node_health_check(node): return data -def inspect_execution_nodes(instance_list): - with advisory_lock('inspect_execution_nodes_lock', wait=False): +def inspect_established_receptor_connections(mesh_status): + ''' + Flips link state from ADDING to ESTABLISHED + If the InstanceLink source and target match the entries + in Known Connection Costs, flip to Established. + ''' + from awx.main.models import InstanceLink + + all_links = InstanceLink.objects.filter(link_state=InstanceLink.States.ADDING) + if not all_links.exists(): + return + active_receptor_conns = mesh_status['KnownConnectionCosts'] + update_links = [] + for link in all_links: + if link.link_state != InstanceLink.States.REMOVING: + if link.target.instance.hostname in active_receptor_conns.get(link.source.hostname, {}): + if link.link_state is not InstanceLink.States.ESTABLISHED: + link.link_state = InstanceLink.States.ESTABLISHED + update_links.append(link) + + InstanceLink.objects.bulk_update(update_links, ['link_state']) + + +def inspect_execution_and_hop_nodes(instance_list): + with advisory_lock('inspect_execution_and_hop_nodes_lock', wait=False): node_lookup = {inst.hostname: inst for inst in instance_list} + try: + ctl = get_receptor_ctl() + except FileNotFoundError: + logger.error('Receptor daemon not running, skipping execution node check') + return + try: + mesh_status = ctl.simple_command('status') + except ValueError as exc: + logger.error(f'Error running receptorctl status command, error: {str(exc)}') + return - ctl = get_receptor_ctl() - mesh_status = ctl.simple_command('status') + inspect_established_receptor_connections(mesh_status) nowtime = now() workers = mesh_status['Advertisements'] @@ -496,8 +621,81 @@ def inspect_execution_nodes(instance_list): execution_node_health_check.apply_async([hostname]) -@task(queue=get_local_queuename, bind_kwargs=['dispatch_time', 'worker_tasks']) -def cluster_node_heartbeat(dispatch_time=None, worker_tasks=None): +@task(queue=get_task_queuename, bind=True) +def cluster_node_heartbeat(binder): + """ + Dispatcherd implementation. + Uses Control API to get running tasks. + """ + + # Run common instance management logic + this_inst, instance_list, lost_instances = _heartbeat_instance_management() + if this_inst is None: + return # Early return case from instance management + + # Check versions + _heartbeat_check_versions(this_inst, instance_list) + + # Handle lost instances + _heartbeat_handle_lost_instances(lost_instances, this_inst) + + # Get running tasks using dispatcherd API + if binder is None: + logger.debug("Heartbeat finished in startup.") + return + active_task_ids = _get_active_task_ids_from_dispatcherd(binder) + if active_task_ids is None: + logger.warning("No active task IDs retrieved from dispatcherd, skipping reaper") + return # Failed to get task IDs, don't attempt reaping + + # Run local reaper using tasks from dispatcherd + ref_time = now() # No dispatch_time in dispatcherd version + logger.debug(f"Running reaper with {len(active_task_ids)} excluded UUIDs") + reaper.reap(instance=this_inst, excluded_uuids=active_task_ids, ref_time=ref_time) + # If waiting jobs are hanging out, resubmit them + if UnifiedJob.objects.filter(controller_node=settings.CLUSTER_HOST_ID, status='waiting').exists(): + from awx.main.tasks.jobs import dispatch_waiting_jobs + + dispatch_waiting_jobs.apply_async(queue=get_task_queuename()) + + +def _get_active_task_ids_from_dispatcherd(binder): + """ + Retrieve active task IDs from the dispatcherd control API. + + Returns: + list: List of active task UUIDs + None: If there was an error retrieving the data + """ + active_task_ids = [] + try: + + logger.debug("Querying dispatcherd API for running tasks") + data = binder.control('running') + + # Extract UUIDs from the running data + # Process running data: first item is a dict with node_id and task entries + data.pop('node_id', None) + + # Extract task UUIDs from data structure + for task_key, task_value in data.items(): + if isinstance(task_value, dict) and 'uuid' in task_value: + active_task_ids.append(task_value['uuid']) + logger.debug(f"Found active task with UUID: {task_value['uuid']}") + elif isinstance(task_key, str): + # Handle case where UUID might be the key + active_task_ids.append(task_key) + logger.debug(f"Found active task with key: {task_key}") + + logger.debug(f"Retrieved {len(active_task_ids)} active task IDs from dispatcherd") + return active_task_ids + except Exception: + logger.exception("Failed to get running tasks from dispatcherd") + return None + + +def _heartbeat_instance_management(): + """Common logic for heartbeat instance management.""" logger.debug("Cluster node heartbeat task.") nowtime = now() instance_list = list(Instance.objects.filter(node_state__in=(Instance.States.READY, Instance.States.UNAVAILABLE, Instance.States.INSTALLED))) @@ -509,7 +707,7 @@ def cluster_node_heartbeat(dispatch_time=None, worker_tasks=None): this_inst = inst break - inspect_execution_nodes(instance_list) + inspect_execution_and_hop_nodes(instance_list) for inst in list(instance_list): if inst == this_inst: @@ -524,20 +722,26 @@ def cluster_node_heartbeat(dispatch_time=None, worker_tasks=None): this_inst.local_health_check() if startup_event and this_inst.capacity != 0: logger.warning(f'Rejoining the cluster as instance {this_inst.hostname}. Prior last_seen {last_last_seen}') - return + return None, None, None # Early return case elif not last_last_seen: logger.warning(f'Instance does not have recorded last_seen, updating to {nowtime}') elif (nowtime - last_last_seen) > timedelta(seconds=settings.CLUSTER_NODE_HEARTBEAT_PERIOD + 2): logger.warning(f'Heartbeat skew - interval={(nowtime - last_last_seen).total_seconds():.4f}, expected={settings.CLUSTER_NODE_HEARTBEAT_PERIOD}') else: if settings.AWX_AUTO_DEPROVISION_INSTANCES: - (changed, this_inst) = Instance.objects.register(ip_address=os.environ.get('MY_POD_IP'), node_type='control', uuid=settings.SYSTEM_UUID) + changed, this_inst = Instance.objects.register(ip_address=os.environ.get('MY_POD_IP'), node_type='control', node_uuid=settings.SYSTEM_UUID) if changed: logger.warning(f'Recreated instance record {this_inst.hostname} after unexpected removal') this_inst.local_health_check() else: - raise RuntimeError("Cluster Host Not Found: {}".format(settings.CLUSTER_HOST_ID)) - # IFF any node has a greater version than we do, then we'll shutdown services + logger.error("Cluster Host Not Found: {}".format(settings.CLUSTER_HOST_ID)) + return None, None, None + + return this_inst, instance_list, lost_instances + + +def _heartbeat_check_versions(this_inst, instance_list): + """Check versions across instances and determine if shutdown is needed.""" for other_inst in instance_list: if other_inst.node_type in ('execution', 'hop'): continue @@ -554,13 +758,18 @@ def cluster_node_heartbeat(dispatch_time=None, worker_tasks=None): stop_local_services(communicate=False) raise RuntimeError("Shutting down.") + +def _heartbeat_handle_lost_instances(lost_instances, this_inst): + """Handle lost instances by reaping their running jobs and marking them offline.""" for other_inst in lost_instances: try: + # Any jobs marked as running will be marked as error explanation = "Job reaped due to instance shutdown" reaper.reap(other_inst, job_explanation=explanation) - reaper.reap_waiting(other_inst, grace_period=0, job_explanation=explanation) + # Any jobs that were waiting to be processed by this node will be handed back to task manager + UnifiedJob.objects.filter(status='waiting', controller_node=other_inst.hostname).update(status='pending', controller_node='', execution_node='') except Exception: - logger.exception('failed to reap jobs for {}'.format(other_inst.hostname)) + logger.exception('failed to re-process jobs for lost instance {}'.format(other_inst.hostname)) try: if settings.AWX_AUTO_DEPROVISION_INSTANCES and other_inst.node_type == "control": deprovision_hostname = other_inst.hostname @@ -571,22 +780,21 @@ def cluster_node_heartbeat(dispatch_time=None, worker_tasks=None): logger.error("Host {} last checked in at {}, marked as lost.".format(other_inst.hostname, other_inst.last_seen)) except DatabaseError as e: - if 'did not affect any rows' in str(e): - logger.debug('Another instance has marked {} as lost'.format(other_inst.hostname)) + cause = e.__cause__ + if cause and hasattr(cause, 'sqlstate'): + sqlstate = cause.sqlstate + sqlstate_str = psycopg.errors.lookup(sqlstate) + logger.debug('SQL Error state: {} - {}'.format(sqlstate, sqlstate_str)) + + if sqlstate == psycopg.errors.NoData: + logger.debug('Another instance has marked {} as lost'.format(other_inst.hostname)) + else: + logger.exception("Error marking {} as lost.".format(other_inst.hostname)) else: - logger.exception('Error marking {} as lost'.format(other_inst.hostname)) - - # Run local reaper - if worker_tasks is not None: - active_task_ids = [] - for task_list in worker_tasks.values(): - active_task_ids.extend(task_list) - reaper.reap(instance=this_inst, excluded_uuids=active_task_ids) - if max(len(task_list) for task_list in worker_tasks.values()) <= 1: - reaper.reap_waiting(instance=this_inst, excluded_uuids=active_task_ids, ref_time=datetime.fromisoformat(dispatch_time)) + logger.exception('No SQL state available. Error marking {} as lost'.format(other_inst.hostname)) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=1800, on_duplicate='queue_one') def awx_receptor_workunit_reaper(): """ When an AWX job is launched via receptor, files such as status, stdin, and stdout are created @@ -609,11 +817,21 @@ def awx_receptor_workunit_reaper(): if not settings.RECEPTOR_RELEASE_WORK: return logger.debug("Checking for unreleased receptor work units") - receptor_ctl = get_receptor_ctl() - receptor_work_list = receptor_ctl.simple_command("work list") + try: + receptor_ctl = get_receptor_ctl() + except FileNotFoundError: + logger.info('Receptorctl sockfile not found for workunit reaper, doing nothing') + return + try: + receptor_work_list = receptor_ctl.simple_command("work list") + except ValueError as exc: + logger.info(f'Error getting work list for workunit reaper, error: {str(exc)}') + return unit_ids = [id for id in receptor_work_list] jobs_with_unreleased_receptor_units = UnifiedJob.objects.filter(work_unit_id__in=unit_ids).exclude(status__in=ACTIVE_STATES) + if settings.RECEPTOR_KEEP_WORK_ON_ERROR: + jobs_with_unreleased_receptor_units = jobs_with_unreleased_receptor_units.exclude(status__in=ERROR_STATES) for job in jobs_with_unreleased_receptor_units: logger.debug(f"{job.log_format} is not active, reaping receptor work unit {job.work_unit_id}") receptor_ctl.simple_command(f"work cancel {job.work_unit_id}") @@ -622,7 +840,7 @@ def awx_receptor_workunit_reaper(): administrative_workunit_reaper(receptor_work_list) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=1800, on_duplicate='queue_one') def awx_k8s_reaper(): if not settings.RECEPTOR_RELEASE_WORK: return @@ -633,7 +851,10 @@ def awx_k8s_reaper(): logger.debug("Checking for orphaned k8s pods for {}.".format(group)) pods = PodManager.list_active_jobs(group) time_cutoff = now() - timedelta(seconds=settings.K8S_POD_REAPER_GRACE_PERIOD) - for job in UnifiedJob.objects.filter(pk__in=pods.keys(), finished__lte=time_cutoff).exclude(status__in=ACTIVE_STATES): + reap_job_candidates = UnifiedJob.objects.filter(pk__in=pods.keys(), finished__lte=time_cutoff).exclude(status__in=ACTIVE_STATES) + if settings.RECEPTOR_KEEP_WORK_ON_ERROR: + reap_job_candidates = reap_job_candidates.exclude(status__in=ERROR_STATES) + for job in reap_job_candidates: logger.debug('{} is no longer active, reaping orphaned k8s pod'.format(job.log_format)) try: pm = PodManager(job) @@ -642,9 +863,10 @@ def awx_k8s_reaper(): logger.exception("Failed to delete orphaned pod {} from {}".format(job.log_format, group)) -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=3600 * 5, on_duplicate='discard') def awx_periodic_scheduler(): - with advisory_lock('awx_periodic_scheduler_lock', wait=False) as acquired: + lock_session_timeout_milliseconds = settings.TASK_MANAGER_LOCK_TIMEOUT * 1000 + with advisory_lock('awx_periodic_scheduler_lock', lock_session_timeout_milliseconds=lock_session_timeout_milliseconds, wait=False) as acquired: if acquired is False: logger.debug("Not running periodic scheduler, another task holds lock") return @@ -691,76 +913,29 @@ def awx_periodic_scheduler(): continue if not can_start: new_unified_job.status = 'failed' - new_unified_job.job_explanation = gettext_noop( - "Scheduled job could not start because it \ - was not in the right state or required manual credentials" - ) + new_unified_job.job_explanation = gettext_noop("Scheduled job could not start because it \ + was not in the right state or required manual credentials") new_unified_job.save(update_fields=['status', 'job_explanation']) new_unified_job.websocket_emit_status("failed") emit_channel_notification('schedules-changed', dict(id=schedule.id, group_name="schedules")) - state.save() - - -def schedule_manager_success_or_error(instance): - if instance.unifiedjob_blocked_jobs.exists(): - ScheduleTaskManager().schedule() - if instance.spawned_by_workflow: - ScheduleWorkflowManager().schedule() - - -@task(queue=get_local_queuename) -def handle_work_success(task_actual): - try: - instance = UnifiedJob.get_instance_by_type(task_actual['type'], task_actual['id']) - except ObjectDoesNotExist: - logger.warning('Missing {} `{}` in success callback.'.format(task_actual['type'], task_actual['id'])) - return - if not instance: - return - schedule_manager_success_or_error(instance) -@task(queue=get_local_queuename) -def handle_work_error(task_actual): - try: - instance = UnifiedJob.get_instance_by_type(task_actual['type'], task_actual['id']) - except ObjectDoesNotExist: - logger.warning('Missing {} `{}` in error callback.'.format(task_actual['type'], task_actual['id'])) - return - if not instance: - return - - subtasks = instance.get_jobs_fail_chain() # reverse of dependent_jobs mostly - logger.debug(f'Executing error task id {task_actual["id"]}, subtasks: {[subtask.id for subtask in subtasks]}') - - deps_of_deps = {} - - for subtask in subtasks: - if subtask.celery_task_id != instance.celery_task_id and not subtask.cancel_flag and not subtask.status in ('successful', 'failed'): - # If there are multiple in the dependency chain, A->B->C, and this was called for A, blame B for clarity - blame_job = deps_of_deps.get(subtask.id, instance) - subtask.status = 'failed' - subtask.failed = True - if not subtask.job_explanation: - subtask.job_explanation = 'Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % ( - get_type_for_model(type(blame_job)), - blame_job.name, - blame_job.id, - ) - subtask.save() - subtask.websocket_emit_status("failed") - - for sub_subtask in subtask.get_jobs_fail_chain(): - deps_of_deps[sub_subtask.id] = subtask - - # We only send 1 job complete message since all the job completion message - # handling does is trigger the scheduler. If we extend the functionality of - # what the job complete message handler does then we may want to send a - # completion event for each job here. - schedule_manager_success_or_error(instance) +@task(queue=get_task_queuename, timeout=3600) +def handle_failure_notifications(task_ids): + """A task-ified version of the method that sends notifications.""" + found_task_ids = set() + for instance in UnifiedJob.objects.filter(id__in=task_ids): + found_task_ids.add(instance.id) + try: + instance.send_notification_templates('failed') + except Exception: + logger.exception(f'Error preparing notifications for task {instance.id}') + deleted_tasks = set(task_ids) - found_task_ids + if deleted_tasks: + logger.warning(f'Could not send notifications for {deleted_tasks} because they were not found in the database') -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=3600 * 5) def update_inventory_computed_fields(inventory_id): """ Signal handler and wrapper around inventory.update_computed_fields to @@ -774,10 +949,19 @@ def update_inventory_computed_fields(inventory_id): try: i.update_computed_fields() except DatabaseError as e: - if 'did not affect any rows' in str(e): - logger.debug('Exiting duplicate update_inventory_computed_fields task.') - return - raise + # https://github.com/django/django/blob/eff21d8e7a1cb297aedf1c702668b590a1b618f3/django/db/models/base.py#L1105 + # django raises DatabaseError("Forced update did not affect any rows.") + + # if sqlstate is set then there was a database error and otherwise will re-raise that error + cause = e.__cause__ + if cause and hasattr(cause, 'sqlstate'): + sqlstate = cause.sqlstate + sqlstate_str = psycopg.errors.lookup(sqlstate) + logger.error('SQL Error state: {} - {}'.format(sqlstate, sqlstate_str)) + raise + + # otherwise + logger.debug('Exiting duplicate update_inventory_computed_fields task.') def update_smart_memberships_for_inventory(smart_inventory): @@ -801,7 +985,7 @@ def update_smart_memberships_for_inventory(smart_inventory): return False -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=3600, on_duplicate='queue_one') def update_host_smart_inventory_memberships(): smart_inventories = Inventory.objects.filter(kind='smart', host_filter__isnull=False, pending_deletion=False) changed_inventories = set([]) @@ -817,7 +1001,7 @@ def update_host_smart_inventory_memberships(): smart_inventory.update_computed_fields() -@task(queue=get_local_queuename) +@task(queue=get_task_queuename, timeout=3600 * 5) def delete_inventory(inventory_id, user_id, retries=5): # Delete inventory as user if user_id is None: @@ -829,10 +1013,7 @@ def delete_inventory(inventory_id, user_id, retries=5): user = None with ignore_inventory_computed_fields(), ignore_inventory_group_removal(), impersonate(user): try: - i = Inventory.objects.get(id=inventory_id) - for host in i.hosts.iterator(): - host.job_events_as_primary_host.update(host=None) - i.delete() + Inventory.objects.get(id=inventory_id).delete() emit_channel_notification('inventories-status_changed', {'group_name': 'inventories', 'inventory_id': inventory_id, 'status': 'deleted'}) logger.debug('Deleted inventory {} as user {}.'.format(inventory_id, user_id)) except Inventory.DoesNotExist: @@ -882,16 +1063,9 @@ def _reconstruct_relationships(copy_mapping): new_obj.save() -@task(queue=get_local_queuename) -def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, uuid, permission_check_func=None): - sub_obj_list = cache.get(uuid) - if sub_obj_list is None: - logger.error('Deep copy {} from {} to {} failed unexpectedly.'.format(model_name, obj_pk, new_obj_pk)) - return - +@task(queue=get_task_queuename, timeout=600) +def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, permission_check_func=None): logger.debug('Deep copy {} from {} to {}.'.format(model_name, obj_pk, new_obj_pk)) - from awx.api.generics import CopyAPIView - from awx.main.signals import disable_activity_stream model = getattr(importlib.import_module(model_module), model_name, None) if model is None: @@ -903,6 +1077,28 @@ def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, u except ObjectDoesNotExist: logger.warning("Object or user no longer exists.") return + + o2m_to_preserve = {} + fields_to_preserve = set(getattr(model, 'FIELDS_TO_PRESERVE_AT_COPY', [])) + + for field in model._meta.get_fields(): + if field.name in fields_to_preserve: + if field.one_to_many: + try: + field_val = getattr(obj, field.name) + except AttributeError: + continue + o2m_to_preserve[field.name] = field_val + + sub_obj_list = [] + for o2m in o2m_to_preserve: + for sub_obj in o2m_to_preserve[o2m].all(): + sub_model = type(sub_obj) + sub_obj_list.append((sub_model.__module__, sub_model.__name__, sub_obj.pk)) + + from awx.api.generics import CopyAPIView + from awx.main.signals import disable_activity_stream + with transaction.atomic(), ignore_inventory_computed_fields(), disable_activity_stream(): copy_mapping = {} for sub_obj_setup in sub_obj_list: @@ -920,3 +1116,27 @@ def deep_copy_model_obj(model_module, model_name, obj_pk, new_obj_pk, user_pk, u permission_check_func(creater, copy_mapping.values()) if isinstance(new_obj, Inventory): update_inventory_computed_fields.delay(new_obj.id) + + +@task(queue=get_task_queuename, timeout=3600, on_duplicate='discard') +def periodic_resource_sync(): + if not getattr(settings, 'RESOURCE_SERVER', None): + logger.debug("Skipping periodic resource_sync, RESOURCE_SERVER not configured") + return + + with advisory_lock('periodic_resource_sync', wait=False) as acquired: + if acquired is False: + logger.debug("Not running periodic_resource_sync, another task holds lock") + return + logger.debug("Running periodic resource sync") + + executor = SyncExecutor() + executor.run() + for key, item_list in executor.results.items(): + if not item_list or key == 'noop': + continue + # Log creations and conflicts + if len(item_list) > 10 and settings.LOG_AGGREGATOR_LEVEL != 'DEBUG': + logger.info(f'Periodic resource sync {key}, first 10 items:\n{item_list[:10]}') + else: + logger.info(f'Periodic resource sync {key}:\n{item_list}') diff --git a/awx/main/tests/README.md b/awx/main/tests/README.md new file mode 100644 index 000000000000..f6aac6418eec --- /dev/null +++ b/awx/main/tests/README.md @@ -0,0 +1,42 @@ +## Test Environments + +Several of the subfolders of `awx/main/tests/` indicate a different required _environment_ +where you can run the tests. Those folders are: + + - `functional/` - requires a test database and no other services running + - `live/` - must run in `tools_awx_1` container launched by `make docker-compose` + - `unit/` - does not require a test database or any active services + +### Functional and unit test environment + +The functional and unit tests have an invocation in `make test`, +and this attaches several other things like schema that piggybacks on requests. +These tests are ran from the root AWX folder. + +#### Functional tests + +Only tests in the `functional/` folder should use the `@pytest.mark.django_db` decorator. +This is the only difference between the functional and unit folders, +the test environment is otherwise the same for both. + +Functional tests use a sqlite3 database, so the postgres service is not necessary. + +### Live tests + +The live tests have an invocation in `make live_test` which will change +directory before running, which is required to pick up a different pytest +configuration. + +This will use the postges container from `make docker-compose` for the database, +and will disable the pytest-django features of running with a test database +and running tests in transactions. +This means that any changes done in the course of the test could potentially +be seen in your browser via the API or UI, and anything the test fails +to clean up will remain in the database. + +### Folders that should not contain tests + + - `data/` - just files other tests use + - `docs/` - utilities for schema generation + - `factories/` - general utilities + - `manual/` - python files to be ran directly diff --git a/awx/main/tests/conftest.py b/awx/main/tests/conftest.py index 28565901b06a..71e9637ed9e8 100644 --- a/awx/main/tests/conftest.py +++ b/awx/main/tests/conftest.py @@ -140,11 +140,6 @@ def rf(persisted): return rf -@pytest.fixture -def job_with_secret_key_unit(job_with_secret_key_factory): - return job_with_secret_key_factory(persisted=False) - - @pytest.fixture def workflow_job_template_factory(): return create_workflow_job_template @@ -216,6 +211,25 @@ def event_qs(self): @pytest.fixture def mock_me(): + "Allows Instance.objects.me() to work without touching the database" me_mock = mock.MagicMock(return_value=Instance(id=1, hostname=settings.CLUSTER_HOST_ID, uuid='00000000-0000-0000-0000-000000000000')) with mock.patch.object(Instance.objects, 'me', me_mock): yield + + +@pytest.fixture +def me_inst(): + "Inserts an instance to the database for Instance.objects.me(), and goes ahead and mocks it in" + inst = Instance.objects.create(hostname='local_node', uuid='00000000-0000-0000-0000-000000000000') + me_mock = mock.MagicMock(return_value=inst) + with mock.patch.object(Instance.objects, 'me', me_mock): + yield inst + + +@pytest.fixture(scope="session", autouse=True) +def load_all_credentials(): + with mock.patch('awx.main.models.credential.detect_server_product_name', return_value='NOT_AWX'): + from awx.main.models.credential import load_credentials + + load_credentials() + yield diff --git a/awx/main/tests/data/ansible_utils/playbooks/valid/hello_world.yml b/awx/main/tests/data/ansible_utils/playbooks/valid/hello_world.yml index 80d56debc40a..7aff8dbf9ef4 100644 --- a/awx/main/tests/data/ansible_utils/playbooks/valid/hello_world.yml +++ b/awx/main/tests/data/ansible_utils/playbooks/valid/hello_world.yml @@ -3,5 +3,5 @@ hosts: all tasks: - name: Hello Message - debug: + ansible.builtin.debug: msg: "Hello World!" diff --git a/awx/main/tests/data/collections/host_query_external_v1_0_0/galaxy.yml b/awx/main/tests/data/collections/host_query_external_v1_0_0/galaxy.yml new file mode 100644 index 000000000000..d93afda096e6 --- /dev/null +++ b/awx/main/tests/data/collections/host_query_external_v1_0_0/galaxy.yml @@ -0,0 +1,19 @@ +--- +authors: + - AWX Project Contributors +dependencies: {} +description: External query testing collection. No embedded query file. Not for use in production. +documentation: https://github.com/ansible/awx +homepage: https://github.com/ansible/awx +issues: https://github.com/ansible/awx +license: + - GPL-3.0-or-later +name: external +namespace: demo +readme: README.md +repository: https://github.com/ansible/awx +tags: + - demo + - testing + - external_query +version: 1.0.0 diff --git a/awx/main/tests/data/collections/host_query_external_v1_0_0/plugins/modules/example.py b/awx/main/tests/data/collections/host_query_external_v1_0_0/plugins/modules/example.py new file mode 100644 index 000000000000..aea6b4352c81 --- /dev/null +++ b/awx/main/tests/data/collections/host_query_external_v1_0_0/plugins/modules/example.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +# Same licensing as AWX +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: example + +short_description: Module for specific live tests + +version_added: "2.0.0" + +description: This module is part of a test collection in local source. Used for external query testing. + +options: + host_name: + description: Name to return as the host name. + required: false + type: str + +author: + - AWX Live Tests +''' + +EXAMPLES = r''' +- name: Test with defaults + demo.external.example: + +- name: Test with custom host name + demo.external.example: + host_name: foo_host +''' + +RETURN = r''' +direct_host_name: + description: The name of the host, this will be collected with the feature. + type: str + returned: always + sample: 'foo_host' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def run_module(): + module_args = dict( + host_name=dict(type='str', required=False, default='foo_host_default'), + ) + + result = dict( + changed=False, + other_data='sample_string', + ) + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + + if module.check_mode: + module.exit_json(**result) + + result['direct_host_name'] = module.params['host_name'] + result['nested_host_name'] = {'host_name': module.params['host_name']} + result['name'] = 'vm-foo' + + # non-cononical facts + result['device_type'] = 'Fake Host' + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/awx/main/tests/data/collections/host_query_external_v1_5_0/galaxy.yml b/awx/main/tests/data/collections/host_query_external_v1_5_0/galaxy.yml new file mode 100644 index 000000000000..a2dcb358f552 --- /dev/null +++ b/awx/main/tests/data/collections/host_query_external_v1_5_0/galaxy.yml @@ -0,0 +1,19 @@ +--- +authors: + - AWX Project Contributors +dependencies: {} +description: External query testing collection v1.5.0. No embedded query file. Not for use in production. +documentation: https://github.com/ansible/awx +homepage: https://github.com/ansible/awx +issues: https://github.com/ansible/awx +license: + - GPL-3.0-or-later +name: external +namespace: demo +readme: README.md +repository: https://github.com/ansible/awx +tags: + - demo + - testing + - external_query +version: 1.5.0 diff --git a/awx/main/tests/data/collections/host_query_external_v1_5_0/plugins/modules/example.py b/awx/main/tests/data/collections/host_query_external_v1_5_0/plugins/modules/example.py new file mode 100644 index 000000000000..aea6b4352c81 --- /dev/null +++ b/awx/main/tests/data/collections/host_query_external_v1_5_0/plugins/modules/example.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +# Same licensing as AWX +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: example + +short_description: Module for specific live tests + +version_added: "2.0.0" + +description: This module is part of a test collection in local source. Used for external query testing. + +options: + host_name: + description: Name to return as the host name. + required: false + type: str + +author: + - AWX Live Tests +''' + +EXAMPLES = r''' +- name: Test with defaults + demo.external.example: + +- name: Test with custom host name + demo.external.example: + host_name: foo_host +''' + +RETURN = r''' +direct_host_name: + description: The name of the host, this will be collected with the feature. + type: str + returned: always + sample: 'foo_host' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def run_module(): + module_args = dict( + host_name=dict(type='str', required=False, default='foo_host_default'), + ) + + result = dict( + changed=False, + other_data='sample_string', + ) + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + + if module.check_mode: + module.exit_json(**result) + + result['direct_host_name'] = module.params['host_name'] + result['nested_host_name'] = {'host_name': module.params['host_name']} + result['name'] = 'vm-foo' + + # non-cononical facts + result['device_type'] = 'Fake Host' + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/awx/main/tests/data/collections/host_query_external_v3_0_0/galaxy.yml b/awx/main/tests/data/collections/host_query_external_v3_0_0/galaxy.yml new file mode 100644 index 000000000000..6a4bb64ecba1 --- /dev/null +++ b/awx/main/tests/data/collections/host_query_external_v3_0_0/galaxy.yml @@ -0,0 +1,19 @@ +--- +authors: + - AWX Project Contributors +dependencies: {} +description: External query testing collection v3.0.0. No embedded query file. Not for use in production. +documentation: https://github.com/ansible/awx +homepage: https://github.com/ansible/awx +issues: https://github.com/ansible/awx +license: + - GPL-3.0-or-later +name: external +namespace: demo +readme: README.md +repository: https://github.com/ansible/awx +tags: + - demo + - testing + - external_query +version: 3.0.0 diff --git a/awx/main/tests/data/collections/host_query_external_v3_0_0/plugins/modules/example.py b/awx/main/tests/data/collections/host_query_external_v3_0_0/plugins/modules/example.py new file mode 100644 index 000000000000..aea6b4352c81 --- /dev/null +++ b/awx/main/tests/data/collections/host_query_external_v3_0_0/plugins/modules/example.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +# Same licensing as AWX +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: example + +short_description: Module for specific live tests + +version_added: "2.0.0" + +description: This module is part of a test collection in local source. Used for external query testing. + +options: + host_name: + description: Name to return as the host name. + required: false + type: str + +author: + - AWX Live Tests +''' + +EXAMPLES = r''' +- name: Test with defaults + demo.external.example: + +- name: Test with custom host name + demo.external.example: + host_name: foo_host +''' + +RETURN = r''' +direct_host_name: + description: The name of the host, this will be collected with the feature. + type: str + returned: always + sample: 'foo_host' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def run_module(): + module_args = dict( + host_name=dict(type='str', required=False, default='foo_host_default'), + ) + + result = dict( + changed=False, + other_data='sample_string', + ) + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + + if module.check_mode: + module.exit_json(**result) + + result['direct_host_name'] = module.params['host_name'] + result['nested_host_name'] = {'host_name': module.params['host_name']} + result['name'] = 'vm-foo' + + # non-cononical facts + result['device_type'] = 'Fake Host' + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/awx/main/tests/data/inventory/plugins/azure_rm/env.json b/awx/main/tests/data/inventory/plugins/azure_rm/env.json index 9ad6db311ec0..b2627d437d7c 100644 --- a/awx/main/tests/data/inventory/plugins/azure_rm/env.json +++ b/awx/main/tests/data/inventory/plugins/azure_rm/env.json @@ -1,9 +1,8 @@ { "ANSIBLE_JINJA2_NATIVE": "True", - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "AZURE_CLIENT_ID": "fooo", "AZURE_CLOUD_ENVIRONMENT": "fooo", "AZURE_SECRET": "fooo", "AZURE_SUBSCRIPTION_ID": "fooo", "AZURE_TENANT": "fooo" -} \ No newline at end of file +} diff --git a/awx/main/tests/data/inventory/plugins/controller/env.json b/awx/main/tests/data/inventory/plugins/controller/env.json index cc7a5d1ffa7d..8a13483b0462 100644 --- a/awx/main/tests/data/inventory/plugins/controller/env.json +++ b/awx/main/tests/data/inventory/plugins/controller/env.json @@ -1,5 +1,4 @@ { - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "TOWER_HOST": "https://foo.invalid", "TOWER_PASSWORD": "fooo", "TOWER_USERNAME": "fooo", @@ -9,5 +8,12 @@ "CONTROLLER_PASSWORD": "fooo", "CONTROLLER_USERNAME": "fooo", "CONTROLLER_OAUTH_TOKEN": "", - "CONTROLLER_VERIFY_SSL": "False" -} \ No newline at end of file + "CONTROLLER_VERIFY_SSL": "False", + "AAP_HOSTNAME": "https://foo.invalid", + "AAP_PASSWORD": "fooo", + "AAP_USERNAME": "fooo", + "AAP_VALIDATE_CERTS": "False", + "CONTROLLER_REQUEST_TIMEOUT": "fooo", + "AAP_REQUEST_TIMEOUT": "fooo", + "AAP_TOKEN": "" +} diff --git a/awx/main/tests/data/inventory/plugins/ec2/env.json b/awx/main/tests/data/inventory/plugins/ec2/env.json index 77cedd003eb2..2d1fea36ac12 100644 --- a/awx/main/tests/data/inventory/plugins/ec2/env.json +++ b/awx/main/tests/data/inventory/plugins/ec2/env.json @@ -1,8 +1,7 @@ { "ANSIBLE_JINJA2_NATIVE": "True", - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "AWS_ACCESS_KEY_ID": "fooo", "AWS_SECRET_ACCESS_KEY": "fooo", "AWS_SECURITY_TOKEN": "fooo", "AWS_SESSION_TOKEN": "fooo" -} \ No newline at end of file +} diff --git a/awx/main/tests/data/inventory/plugins/gce/env.json b/awx/main/tests/data/inventory/plugins/gce/env.json index 4c87c078eb88..13970e2356d2 100644 --- a/awx/main/tests/data/inventory/plugins/gce/env.json +++ b/awx/main/tests/data/inventory/plugins/gce/env.json @@ -1,6 +1,5 @@ { "ANSIBLE_JINJA2_NATIVE": "True", - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "GCE_CREDENTIALS_FILE_PATH": "{{ file_reference }}", "GOOGLE_APPLICATION_CREDENTIALS": "{{ file_reference }}", "GCP_AUTH_KIND": "serviceaccount", diff --git a/awx/main/tests/data/inventory/plugins/insights/env.json b/awx/main/tests/data/inventory/plugins/insights/env.json index 46eb0a34e7bb..bbece6a48fcf 100644 --- a/awx/main/tests/data/inventory/plugins/insights/env.json +++ b/awx/main/tests/data/inventory/plugins/insights/env.json @@ -1,5 +1,6 @@ { - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "INSIGHTS_USER": "fooo", - "INSIGHTS_PASSWORD": "fooo" -} \ No newline at end of file + "INSIGHTS_PASSWORD": "fooo", + "INSIGHTS_CLIENT_ID": "fooo", + "INSIGHTS_CLIENT_SECRET": "fooo" +} diff --git a/awx/main/tests/data/inventory/plugins/openshift_virtualization/env.json b/awx/main/tests/data/inventory/plugins/openshift_virtualization/env.json new file mode 100644 index 000000000000..a44de77b88cb --- /dev/null +++ b/awx/main/tests/data/inventory/plugins/openshift_virtualization/env.json @@ -0,0 +1,5 @@ +{ + "K8S_AUTH_HOST": "https://foo.invalid", + "K8S_AUTH_API_KEY": "fooo", + "K8S_AUTH_VERIFY_SSL": "False" +} diff --git a/awx/main/tests/data/inventory/plugins/openstack/env.json b/awx/main/tests/data/inventory/plugins/openstack/env.json index 88dfb239c373..21e151c38bb3 100644 --- a/awx/main/tests/data/inventory/plugins/openstack/env.json +++ b/awx/main/tests/data/inventory/plugins/openstack/env.json @@ -1,4 +1,3 @@ { - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "OS_CLIENT_CONFIG_FILE": "{{ file_reference }}" -} \ No newline at end of file +} diff --git a/awx/main/tests/data/inventory/plugins/rhv/env.json b/awx/main/tests/data/inventory/plugins/rhv/env.json index 08477df16913..1030a591bf47 100644 --- a/awx/main/tests/data/inventory/plugins/rhv/env.json +++ b/awx/main/tests/data/inventory/plugins/rhv/env.json @@ -1,7 +1,6 @@ { - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "OVIRT_INI_PATH": "{{ file_reference }}", "OVIRT_PASSWORD": "fooo", "OVIRT_URL": "https://foo.invalid", "OVIRT_USERNAME": "fooo" -} \ No newline at end of file +} diff --git a/awx/main/tests/data/inventory/plugins/satellite6/env.json b/awx/main/tests/data/inventory/plugins/satellite6/env.json index 102abee70b10..482d2ae5057c 100644 --- a/awx/main/tests/data/inventory/plugins/satellite6/env.json +++ b/awx/main/tests/data/inventory/plugins/satellite6/env.json @@ -1,6 +1,5 @@ { - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "FOREMAN_PASSWORD": "fooo", "FOREMAN_SERVER": "https://foo.invalid", "FOREMAN_USER": "fooo" -} \ No newline at end of file +} diff --git a/awx/main/tests/data/inventory/plugins/terraform/env.json b/awx/main/tests/data/inventory/plugins/terraform/env.json new file mode 100644 index 000000000000..49e8282433e8 --- /dev/null +++ b/awx/main/tests/data/inventory/plugins/terraform/env.json @@ -0,0 +1,3 @@ +{ + "GOOGLE_BACKEND_CREDENTIALS": "{{ file_reference }}" +} diff --git a/awx/main/tests/data/inventory/plugins/vmware/env.json b/awx/main/tests/data/inventory/plugins/vmware/env.json index 97563377c050..6321c24f2632 100644 --- a/awx/main/tests/data/inventory/plugins/vmware/env.json +++ b/awx/main/tests/data/inventory/plugins/vmware/env.json @@ -1,7 +1,6 @@ { - "ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS": "never", "VMWARE_HOST": "https://foo.invalid", "VMWARE_PASSWORD": "fooo", "VMWARE_USER": "fooo", "VMWARE_VALIDATE_CERTS": "False" -} \ No newline at end of file +} diff --git a/awx/main/tests/data/projects/README.md b/awx/main/tests/data/projects/README.md new file mode 100644 index 000000000000..26be7e425c27 --- /dev/null +++ b/awx/main/tests/data/projects/README.md @@ -0,0 +1,41 @@ +# Project data for live tests + +Each folder in this directory is usable as source for a project or role or collection, +which is used in tests, particularly the "awx/main/tests/live" tests. + +Although these are not git repositories, test fixtures will make copies, +and in the coppied folders, run `git init` type commands, turning them into +git repos. This is done in the locations + + - `/var/lib/awx/projects` + - `/tmp/live_tests` + +These can then be referenced for manual projects or git via the `file://` protocol. + +## debug + +This is the simplest possible case with 1 playbook with 1 debug task. + +## with_requirements + +This has a playbook that runs a task that uses a role. + +The role project is referenced in the `roles/requirements.yml` file. + +### role_requirement + +This is the source for the role that the `with_requirements` project uses. + +## test_host_query + +This has a playbook that runs a task from a custom collection module which +is registered for the host query feature. + +The collection is referenced in its `collections/requirements.yml` file. + +### host_query + +This can act as source code for a collection that enables host/event querying. + +It has a `meta/event_query.yml` file, which may provide you an example of how +to implement this in your own collection. diff --git a/awx/main/tests/data/projects/debug/debug.yml b/awx/main/tests/data/projects/debug/debug.yml new file mode 100644 index 000000000000..f4fdcb2f0e51 --- /dev/null +++ b/awx/main/tests/data/projects/debug/debug.yml @@ -0,0 +1,6 @@ +--- +- hosts: all + gather_facts: false + connection: local + tasks: + - debug: msg='hello' diff --git a/awx/main/tests/data/projects/debug/sleep.yml b/awx/main/tests/data/projects/debug/sleep.yml new file mode 100644 index 000000000000..5ae223f29dc2 --- /dev/null +++ b/awx/main/tests/data/projects/debug/sleep.yml @@ -0,0 +1,9 @@ +--- +- hosts: all + gather_facts: false + connection: local + vars: + sleep_interval: 5 + tasks: + - name: sleep for a specified interval + command: sleep '{{ sleep_interval }}' diff --git a/awx/main/tests/data/projects/facts/clear.yml b/awx/main/tests/data/projects/facts/clear.yml new file mode 100644 index 000000000000..140463d6c8fc --- /dev/null +++ b/awx/main/tests/data/projects/facts/clear.yml @@ -0,0 +1,7 @@ +--- + +- hosts: all + gather_facts: false + connection: local + tasks: + - meta: clear_facts diff --git a/awx/main/tests/data/projects/facts/gather.yml b/awx/main/tests/data/projects/facts/gather.yml new file mode 100644 index 000000000000..4b5592dd8b3e --- /dev/null +++ b/awx/main/tests/data/projects/facts/gather.yml @@ -0,0 +1,17 @@ +--- + +- hosts: all + vars: + extra_value: "" + gather_facts: false + connection: local + tasks: + - name: set a custom fact + set_fact: + foo: "bar{{ extra_value }}" + bar: + a: + b: + - "c" + - "d" + cacheable: true diff --git a/awx/main/tests/data/projects/facts/gather_slow.yml b/awx/main/tests/data/projects/facts/gather_slow.yml new file mode 100644 index 000000000000..e39f7f8d4c62 --- /dev/null +++ b/awx/main/tests/data/projects/facts/gather_slow.yml @@ -0,0 +1,21 @@ +--- +# Generated by Claude Opus 4.6 (claude-opus-4-6). + +- hosts: all + vars: + extra_value: "" + gather_facts: false + connection: local + tasks: + - name: set a custom fact + set_fact: + foo: "bar{{ extra_value }}" + bar: + a: + b: + - "c" + - "d" + cacheable: true + - name: sleep to create overlap window for concurrent job testing + wait_for: + timeout: 2 diff --git a/awx/main/tests/data/projects/facts/no_op.yml b/awx/main/tests/data/projects/facts/no_op.yml new file mode 100644 index 000000000000..05e5b244d7d1 --- /dev/null +++ b/awx/main/tests/data/projects/facts/no_op.yml @@ -0,0 +1,9 @@ +--- + +- hosts: all + gather_facts: false + connection: local + vars: + msg: 'hello' + tasks: + - debug: var=msg diff --git a/awx/main/tests/data/projects/host_query/extensions/audit/event_query.yml b/awx/main/tests/data/projects/host_query/extensions/audit/event_query.yml new file mode 100644 index 000000000000..a10586b90e22 --- /dev/null +++ b/awx/main/tests/data/projects/host_query/extensions/audit/event_query.yml @@ -0,0 +1,4 @@ +--- +demo.query.example: + query: >- + {name: .name, canonical_facts: {host_name: .direct_host_name}, facts: {device_type: .device_type}} diff --git a/awx/main/tests/data/projects/host_query/galaxy.yml b/awx/main/tests/data/projects/host_query/galaxy.yml new file mode 100644 index 000000000000..a69203d41643 --- /dev/null +++ b/awx/main/tests/data/projects/host_query/galaxy.yml @@ -0,0 +1,19 @@ +--- +authors: + - AWX Project Contributors +dependencies: {} +description: Indirect host counting example repo. Not for use in production. +documentation: https://github.com/ansible/awx +homepage: https://github.com/ansible/awx +issues: https://github.com/ansible/awx +license: + - GPL-3.0-or-later +name: query +namespace: demo +readme: README.md +repository: https://github.com/ansible/awx +tags: + - demo + - testing + - host_counting +version: 0.0.1 diff --git a/awx/main/tests/data/projects/host_query/plugins/modules/example.py b/awx/main/tests/data/projects/host_query/plugins/modules/example.py new file mode 100644 index 000000000000..c1427c1d52bd --- /dev/null +++ b/awx/main/tests/data/projects/host_query/plugins/modules/example.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +# Same licensing as AWX +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r''' +--- +module: example + +short_description: Module for specific live tests + +version_added: "2.0.0" + +description: This module is part of a test collection in local source. + +options: + host_name: + description: Name to return as the host name. + required: false + type: str + +author: + - AWX Live Tests +''' + +EXAMPLES = r''' +- name: Test with defaults + demo.query.example: + +- name: Test with custom host name + demo.query.example: + host_name: foo_host +''' + +RETURN = r''' +direct_host_name: + description: The name of the host, this will be collected with the feature. + type: str + returned: always + sample: 'foo_host' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def run_module(): + module_args = dict( + host_name=dict(type='str', required=False, default='foo_host_default'), + ) + + result = dict( + changed=False, + other_data='sample_string', + ) + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=True) + + if module.check_mode: + module.exit_json(**result) + + result['direct_host_name'] = module.params['host_name'] + result['nested_host_name'] = {'host_name': module.params['host_name']} + result['name'] = 'vm-foo' + + # non-cononical facts + result['device_type'] = 'Fake Host' + + module.exit_json(**result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main() diff --git a/awx/main/tests/data/projects/inventory_vars/inventory_var_deleted_in_source.ini b/awx/main/tests/data/projects/inventory_vars/inventory_var_deleted_in_source.ini new file mode 100644 index 000000000000..8dfebcd50291 --- /dev/null +++ b/awx/main/tests/data/projects/inventory_vars/inventory_var_deleted_in_source.ini @@ -0,0 +1,3 @@ +[all:vars] +a=value_a +b=value_b diff --git a/awx/main/tests/data/projects/role_requirement/meta/main.yml b/awx/main/tests/data/projects/role_requirement/meta/main.yml new file mode 100644 index 000000000000..25563e6d68c8 --- /dev/null +++ b/awx/main/tests/data/projects/role_requirement/meta/main.yml @@ -0,0 +1,19 @@ +--- +galaxy_info: + author: "For Test" + company: AWX + license: MIT + min_ansible_version: 1.4 + platforms: + - name: EL + versions: + - 8 + - 9 + - name: Fedora + versions: + - 39 + - 40 + - 41 + categories: + - stuff +dependencies: [] diff --git a/awx/main/tests/data/projects/role_requirement/tasks/main.yml b/awx/main/tests/data/projects/role_requirement/tasks/main.yml new file mode 100644 index 000000000000..2dbafc91cfb7 --- /dev/null +++ b/awx/main/tests/data/projects/role_requirement/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- name: debug variable + debug: + msg: "1234567890" diff --git a/awx/main/tests/data/projects/test_host_query/collections/requirements.yml b/awx/main/tests/data/projects/test_host_query/collections/requirements.yml new file mode 100644 index 000000000000..17e176ae3991 --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query/collections/requirements.yml @@ -0,0 +1,5 @@ +--- +collections: + - name: 'file:///tmp/live_tests/host_query' + type: git + version: devel diff --git a/awx/main/tests/data/projects/test_host_query/run_task.yml b/awx/main/tests/data/projects/test_host_query/run_task.yml new file mode 100644 index 000000000000..2d23555c6385 --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query/run_task.yml @@ -0,0 +1,8 @@ +--- +- hosts: all + gather_facts: false + connection: local + tasks: + - demo.query.example: + register: result + - debug: var=result diff --git a/awx/main/tests/data/projects/test_host_query_external_v1_0_0/collections/requirements.yml b/awx/main/tests/data/projects/test_host_query_external_v1_0_0/collections/requirements.yml new file mode 100644 index 000000000000..4855d7414155 --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query_external_v1_0_0/collections/requirements.yml @@ -0,0 +1,5 @@ +--- +collections: + - name: 'file:///tmp/live_tests/host_query_external_v1_0_0' + type: git + version: devel diff --git a/awx/main/tests/data/projects/test_host_query_external_v1_0_0/run_task.yml b/awx/main/tests/data/projects/test_host_query_external_v1_0_0/run_task.yml new file mode 100644 index 000000000000..0ac01bbcf595 --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query_external_v1_0_0/run_task.yml @@ -0,0 +1,8 @@ +--- +- hosts: all + gather_facts: false + connection: local + tasks: + - demo.external.example: + register: result + - debug: var=result diff --git a/awx/main/tests/data/projects/test_host_query_external_v1_5_0/collections/requirements.yml b/awx/main/tests/data/projects/test_host_query_external_v1_5_0/collections/requirements.yml new file mode 100644 index 000000000000..c966ca78354a --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query_external_v1_5_0/collections/requirements.yml @@ -0,0 +1,5 @@ +--- +collections: + - name: 'file:///tmp/live_tests/host_query_external_v1_5_0' + type: git + version: devel diff --git a/awx/main/tests/data/projects/test_host_query_external_v1_5_0/run_task.yml b/awx/main/tests/data/projects/test_host_query_external_v1_5_0/run_task.yml new file mode 100644 index 000000000000..0ac01bbcf595 --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query_external_v1_5_0/run_task.yml @@ -0,0 +1,8 @@ +--- +- hosts: all + gather_facts: false + connection: local + tasks: + - demo.external.example: + register: result + - debug: var=result diff --git a/awx/main/tests/data/projects/test_host_query_external_v3_0_0/collections/requirements.yml b/awx/main/tests/data/projects/test_host_query_external_v3_0_0/collections/requirements.yml new file mode 100644 index 000000000000..126ec7721f4b --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query_external_v3_0_0/collections/requirements.yml @@ -0,0 +1,5 @@ +--- +collections: + - name: 'file:///tmp/live_tests/host_query_external_v3_0_0' + type: git + version: devel diff --git a/awx/main/tests/data/projects/test_host_query_external_v3_0_0/run_task.yml b/awx/main/tests/data/projects/test_host_query_external_v3_0_0/run_task.yml new file mode 100644 index 000000000000..0ac01bbcf595 --- /dev/null +++ b/awx/main/tests/data/projects/test_host_query_external_v3_0_0/run_task.yml @@ -0,0 +1,8 @@ +--- +- hosts: all + gather_facts: false + connection: local + tasks: + - demo.external.example: + register: result + - debug: var=result diff --git a/awx/main/tests/data/projects/with_requirements/roles/requirements.yml b/awx/main/tests/data/projects/with_requirements/roles/requirements.yml new file mode 100644 index 000000000000..b4eb43576f61 --- /dev/null +++ b/awx/main/tests/data/projects/with_requirements/roles/requirements.yml @@ -0,0 +1,3 @@ +--- +- name: role_requirement + src: git+file:///tmp/live_tests/role_requirement diff --git a/awx/main/tests/data/projects/with_requirements/use_requirement.yml b/awx/main/tests/data/projects/with_requirements/use_requirement.yml new file mode 100644 index 000000000000..7907d662d272 --- /dev/null +++ b/awx/main/tests/data/projects/with_requirements/use_requirement.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + connection: local + gather_facts: false + tasks: + - include_role: + name: role_requirement diff --git a/awx/main/tests/data/sleep_task.py b/awx/main/tests/data/sleep_task.py new file mode 100644 index 000000000000..8582a73c794c --- /dev/null +++ b/awx/main/tests/data/sleep_task.py @@ -0,0 +1,55 @@ +import time +import logging + +from dispatcherd.publish import task + +from django.db import connection + +from awx.main.dispatch import get_task_queuename + +from ansible_base.lib.utils.db import advisory_lock + +logger = logging.getLogger(__name__) + + +@task(queue=get_task_queuename) +def sleep_task(seconds=10, log=False): + if log: + logger.info('starting sleep_task') + time.sleep(seconds) + if log: + logger.info('finished sleep_task') + + +@task() +def sleep_break_connection(seconds=0.2): + """ + Interact with the database in an intentionally breaking way. + After this finishes, queries made by this connection are expected to error + with "the connection is closed" + This is obviously a problem for any task that comes afterwards. + So this is used to break things so that the fixes may be demonstrated. + """ + with connection.cursor() as cursor: + cursor.execute(f"SET idle_session_timeout = '{seconds / 2}s';") + + logger.info(f'sleeping for {seconds}s > {seconds / 2}s session timeout') + time.sleep(seconds) + + for i in range(1, 3): + logger.info(f'\nRunning query number {i}') + try: + with connection.cursor() as cursor: + cursor.execute("SELECT 1;") + logger.info(' query worked, not expected') + except Exception as exc: + logger.info(f' query errored as expected\ntype: {type(exc)}\nstr: {str(exc)}') + + logger.info(f'Connection present: {bool(connection.connection)}, reports closed: {getattr(connection.connection, "closed", "not_found")}') + + +@task() +def advisory_lock_exception(): + time.sleep(0.2) # so it can fill up all the workers... hacky for now + with advisory_lock('advisory_lock_exception', lock_session_timeout_milliseconds=20): + raise RuntimeError('this is an intentional error') diff --git a/awx/main/tests/docs/conftest.py b/awx/main/tests/docs/conftest.py index bd0cf1c99ff2..7ec4273627e3 100644 --- a/awx/main/tests/docs/conftest.py +++ b/awx/main/tests/docs/conftest.py @@ -1,13 +1,8 @@ from awx.main.tests.functional.conftest import * # noqa +import os +import pytest -def pytest_addoption(parser): - parser.addoption("--release", action="store", help="a release version number, e.g., 3.3.0") - - -def pytest_generate_tests(metafunc): - # This is called for every test. Only get/set command line arguments - # if the argument is specified in the list of test "fixturenames". - option_value = metafunc.config.option.release - if 'release' in metafunc.fixturenames and option_value is not None: - metafunc.parametrize("release", [option_value]) +@pytest.fixture() +def release(): + return os.environ.get('VERSION_TARGET', '') diff --git a/awx/main/tests/docs/test_swagger_generation.py b/awx/main/tests/docs/test_swagger_generation.py index 658d8ad2d4b5..39b8b8408ae7 100644 --- a/awx/main/tests/docs/test_swagger_generation.py +++ b/awx/main/tests/docs/test_swagger_generation.py @@ -7,7 +7,6 @@ from django.utils.functional import Promise from django.utils.encoding import force_str -from openapi_codec.encode import generate_swagger_object import pytest from awx.api.versioning import drf_reverse @@ -43,12 +42,12 @@ class TestSwaggerGeneration: @pytest.fixture(autouse=True, scope='function') def _prepare(self, get, admin): if not self.__class__.JSON: - url = drf_reverse('api:swagger_view') + '?format=openapi' + # drf-spectacular returns OpenAPI schema directly from schema endpoint + url = drf_reverse('api:schema-json') + '?format=json' response = get(url, user=admin) - data = generate_swagger_object(response.data) + data = response.data if response.has_header('X-Deprecated-Paths'): data['deprecated_paths'] = json.loads(response['X-Deprecated-Paths']) - data.update(response.accepted_renderer.get_customizations() or {}) data['host'] = None data['schemes'] = ['https'] @@ -60,12 +59,21 @@ def _prepare(self, get, admin): # change {version} in paths to the actual default API version (e.g., v2) revised_paths[path.replace('{version}', settings.REST_FRAMEWORK['DEFAULT_VERSION'])] = node for method in node: + # Ignore any parameters methods, these cause issues because it can come as an array instead of a dict + # Which causes issues in the last for loop in here + if method == 'parameters': + continue + if path in deprecated_paths: node[method]['deprecated'] = True if 'description' in node[method]: # Pop off the first line and use that as the summary lines = node[method]['description'].splitlines() - node[method]['summary'] = lines.pop(0).strip('#:') + # If there was a description then set the summary as the description, otherwise make something up + if lines: + node[method]['summary'] = lines.pop(0).strip('#:') + else: + node[method]['summary'] = f'No Description for {method} on {path}' node[method]['description'] = '\n'.join(lines) # remove the required `version` parameter @@ -80,7 +88,7 @@ def test_sanity(self, release, request): JSON['info']['version'] = release if not request.config.getoption('--genschema'): - JSON['modified'] = datetime.datetime.utcnow().isoformat() + JSON['modified'] = datetime.datetime.now(datetime.UTC).isoformat() # Make some basic assertions about the rendered JSON so we can # be sure it doesn't break across DRF upgrades and view/serializer @@ -90,13 +98,13 @@ def test_sanity(self, release, request): # The number of API endpoints changes over time, but let's just check # for a reasonable number here; if this test starts failing, raise/lower the bounds paths = JSON['paths'] - assert 250 < len(paths) < 350 - assert list(paths['/api/'].keys()) == ['get'] - assert list(paths['/api/v2/'].keys()) == ['get'] - assert list(sorted(paths['/api/v2/credentials/'].keys())) == ['get', 'post'] - assert list(sorted(paths['/api/v2/credentials/{id}/'].keys())) == ['delete', 'get', 'patch', 'put'] - assert list(paths['/api/v2/settings/'].keys()) == ['get'] - assert list(paths['/api/v2/settings/{category_slug}/'].keys()) == ['get', 'put', 'patch', 'delete'] + assert 250 < len(paths) < 400 + assert set(list(paths['/api/'].keys())) == set(['get', 'parameters']) + assert set(list(paths['/api/v2/'].keys())) == set(['get', 'parameters']) + assert set(list(sorted(paths['/api/v2/credentials/'].keys()))) == set(['get', 'post', 'parameters']) + assert set(list(sorted(paths['/api/v2/credentials/{id}/'].keys()))) == set(['delete', 'get', 'patch', 'put', 'parameters']) + assert set(list(paths['/api/v2/settings/'].keys())) == set(['get', 'parameters']) + assert set(list(paths['/api/v2/settings/{category_slug}/'].keys())) == set(['get', 'put', 'patch', 'delete', 'parameters']) @pytest.mark.parametrize( 'path', @@ -162,4 +170,8 @@ def teardown_class(cls): data = re.sub(r'[0-9]{4}-[0-9]{2}-[0-9]{2}(T|\s)[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]+(Z|\+[0-9]{2}:[0-9]{2})?', r'2018-02-01T08:00:00.000000Z', data) data = re.sub(r'''(\s+"client_id": ")([a-zA-Z0-9]{40})("\,\s*)''', r'\1xxxx\3', data) data = re.sub(r'"action_node": "[^"]+"', '"action_node": "awx"', data) + + # replace uuids to prevent needless diffs + pattern = r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' + data = re.sub(pattern, r'00000000-0000-0000-0000-000000000000', data) f.write(data) diff --git a/awx/main/tests/factories/fixtures.py b/awx/main/tests/factories/fixtures.py index 27556d6efe47..6f9a3263ac7f 100644 --- a/awx/main/tests/factories/fixtures.py +++ b/awx/main/tests/factories/fixtures.py @@ -1,6 +1,9 @@ import json from django.contrib.auth.models import User +from django.core.exceptions import ValidationError + +from unittest import mock from awx.main.models import ( Organization, @@ -20,6 +23,7 @@ WorkflowJobNode, WorkflowJobTemplateNode, ) +from awx.main.models.inventory import HostMetric, HostMetricSummaryMonthly # mk methods should create only a single object of a single type. # they should also have the option of being persisted or not. @@ -95,11 +99,19 @@ def mk_user(name, is_superuser=False, organization=None, team=None, persisted=Tr def mk_project(name, organization=None, description=None, persisted=True): description = description or '{}-description'.format(name) - project = Project(name=name, description=description, playbook_files=['helloworld.yml', 'alt-helloworld.yml']) + project = Project( + name=name, + description=description, + playbook_files=['helloworld.yml', 'alt-helloworld.yml'], + scm_type='git', + scm_url='https://foo.invalid', + scm_revision='1234567890123456789012345678901234567890', + scm_update_on_launch=False, + ) if organization is not None: project.organization = organization if persisted: - project.save() + project.save(skip_update=True) return project @@ -248,3 +260,42 @@ def mk_workflow_job_node(unified_job_template=None, success_nodes=None, failure_ if persisted: workflow_node.save() return workflow_node + + +def mk_host_metric(hostname, first_automation, last_automation=None, last_deleted=None, deleted=False, persisted=True): + ok, idx = False, 1 + while not ok: + try: + with mock.patch("django.utils.timezone.now") as mock_now: + mock_now.return_value = first_automation + metric = HostMetric( + hostname=hostname or f"host-{first_automation}-{idx}", + first_automation=first_automation, + last_automation=last_automation or first_automation, + last_deleted=last_deleted, + deleted=deleted, + ) + metric.validate_unique() + if persisted: + metric.save() + ok = True + except ValidationError as e: + # Repeat create for auto-generated hostname + if not hostname and e.message_dict.get('hostname', None): + idx += 1 + else: + raise e + + +def mk_host_metric_summary(date, license_consumed=0, license_capacity=0, hosts_added=0, hosts_deleted=0, indirectly_managed_hosts=0, persisted=True): + summary = HostMetricSummaryMonthly( + date=date, + license_consumed=license_consumed, + license_capacity=license_capacity, + hosts_added=hosts_added, + hosts_deleted=hosts_deleted, + indirectly_managed_hosts=indirectly_managed_hosts, + ) + if persisted: + summary.save() + return summary diff --git a/awx/main/tests/functional/__init__.py b/awx/main/tests/functional/__init__.py index 07d89e97390b..3139dcf4e720 100644 --- a/awx/main/tests/functional/__init__.py +++ b/awx/main/tests/functional/__init__.py @@ -59,8 +59,7 @@ def app_post_migration(sender, app_config, **kwargs): elif tblname == 'main_systemjobevent': unique_columns = "system_job_id integer NOT NULL" - cur.execute( - f"""CREATE TABLE _unpartitioned_{tblname} ( + cur.execute(f"""CREATE TABLE _unpartitioned_{tblname} ( id bigint NOT NULL, created timestamp with time zone NOT NULL, modified timestamp with time zone NOT NULL, @@ -72,8 +71,7 @@ def app_post_migration(sender, app_config, **kwargs): uuid character varying(1024) NOT NULL, verbosity integer NOT NULL, {unique_columns}); - """ - ) + """) if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3': diff --git a/awx/main/tests/functional/analytics/test_collectors.py b/awx/main/tests/functional/analytics/test_collectors.py index 0fed6e9c156c..4dcb9cd3c3ec 100644 --- a/awx/main/tests/functional/analytics/test_collectors.py +++ b/awx/main/tests/functional/analytics/test_collectors.py @@ -2,8 +2,8 @@ import tempfile import os import re -import shutil import csv +from io import StringIO from django.utils.timezone import now from datetime import timedelta @@ -20,15 +20,16 @@ ) -@pytest.fixture -def sqlite_copy_expert(request): - # copy_expert is postgres-specific, and SQLite doesn't support it; mock its - # behavior to test that it writes a file that contains stdout from events - path = tempfile.mkdtemp(prefix="copied_tables") +class MockCopy: + headers = None + results = None + sent_data = False - def write_stdout(self, sql, fd): + def __init__(self, sql, parent_connection): # Would be cool if we instead properly disected the SQL query and verified # it that way. But instead, we just take the naive approach here. + self.results = None + self.headers = None sql = sql.strip() assert sql.startswith("COPY (") assert sql.endswith(") TO STDOUT WITH CSV HEADER") @@ -51,29 +52,49 @@ def write_stdout(self, sql, fd): elif not line.endswith(","): sql_new[-1] = sql_new[-1].rstrip(",") sql = "\n".join(sql_new) + parent_connection.execute(sql) + self.results = parent_connection.fetchall() + self.headers = [i[0] for i in parent_connection.description] + + def read(self): + if not self.sent_data: + mem_file = StringIO() + csv_handle = csv.writer( + mem_file, + delimiter=",", + quoting=csv.QUOTE_ALL, + escapechar="\\", + lineterminator="\n", + ) + if self.headers: + csv_handle.writerow(self.headers) + if self.results: + csv_handle.writerows(self.results) + self.sent_data = True + return memoryview((mem_file.getvalue()).encode()) + return None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + - self.execute(sql) - results = self.fetchall() - headers = [i[0] for i in self.description] +@pytest.fixture +def sqlite_copy(request, mocker): + # copy is postgres-specific, and SQLite doesn't support it; mock its + # behavior to test that it writes a file that contains stdout from events - csv_handle = csv.writer( - fd, - delimiter=",", - quoting=csv.QUOTE_ALL, - escapechar="\\", - lineterminator="\n", - ) - csv_handle.writerow(headers) - csv_handle.writerows(results) + def write_stdout(self, sql): + mock_copy = MockCopy(sql, self) + return mock_copy - setattr(SQLiteCursorWrapper, "copy_expert", write_stdout) - request.addfinalizer(lambda: shutil.rmtree(path)) - request.addfinalizer(lambda: delattr(SQLiteCursorWrapper, "copy_expert")) - return path + mocker.patch.object(SQLiteCursorWrapper, 'copy', write_stdout, create=True) @pytest.mark.django_db -def test_copy_tables_unified_job_query(sqlite_copy_expert, project, inventory, job_template): +def test_copy_tables_unified_job_query(sqlite_copy, project, inventory, job_template): """ Ensure that various unified job types are in the output of the query. """ @@ -127,7 +148,7 @@ def workflow_job(states=["new", "new", "new", "new", "new"]): @pytest.mark.django_db -def test_copy_tables_workflow_job_node_query(sqlite_copy_expert, workflow_job): +def test_copy_tables_workflow_job_node_query(sqlite_copy, workflow_job): time_start = now() - timedelta(hours=9) with tempfile.TemporaryDirectory() as tmpdir: diff --git a/awx/main/tests/functional/analytics/test_core.py b/awx/main/tests/functional/analytics/test_core.py index e37f30d26bf1..a2c525c83670 100644 --- a/awx/main/tests/functional/analytics/test_core.py +++ b/awx/main/tests/functional/analytics/test_core.py @@ -2,11 +2,13 @@ import json import os import tarfile +import tempfile from unittest import mock import pytest from django.conf import settings -from awx.main.analytics import gather, register +from django.test.utils import override_settings +from awx.main.analytics import gather, register, ship @register('example', '1.0') @@ -57,3 +59,115 @@ def test_gather(mock_valid_license): os.remove(tgz) except Exception: pass + + +@pytest.fixture +def temp_analytic_tar(): + # Create a temporary file and yield its path + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + temp_file.write(b"data") + temp_file_path = temp_file.name + yield temp_file_path + # Clean up the temporary file after the test + os.remove(temp_file_path) + + +@pytest.fixture +def mock_analytic_post(): + # Patch the Session.post method to return a mock response with status_code 200 + with mock.patch('awx.main.analytics.core.requests.Session.post', return_value=mock.Mock(status_code=200)) as mock_post: + yield mock_post + + +@pytest.mark.parametrize( + "setting_map, expected_result, expected_auth", + [ + # Valid Red Hat credentials + ( + { + 'REDHAT_USERNAME': 'redhat_user', + 'REDHAT_PASSWORD': 'redhat_pass', # NOSONAR + 'SUBSCRIPTIONS_CLIENT_ID': '', + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + True, + ('redhat_user', 'redhat_pass'), + ), + # Valid Subscription credentials with no Red Hat credentials + ( + { + 'REDHAT_USERNAME': None, + 'REDHAT_PASSWORD': None, + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', + 'SUBSCRIPTIONS_CLIENT_SECRET': 'subs_pass', # NOSONAR + }, + True, + ('subs_user', 'subs_pass'), + ), + # Valid Subscription credentials with empty Red Hat credentials + ( + { + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': '', + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', + 'SUBSCRIPTIONS_CLIENT_SECRET': 'subs_pass', # NOSONAR + }, + True, + ('subs_user', 'subs_pass'), + ), + # No credentials + ( + { + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': '', + 'SUBSCRIPTIONS_CLIENT_ID': '', + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + False, + None, # No request should be made + ), + # Mixed credentials + ( + { + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': 'redhat_pass', # NOSONAR + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + False, + None, # Invalid, no request should be made + ), + ], +) +@pytest.mark.django_db +def test_ship_credential(setting_map, expected_result, expected_auth, temp_analytic_tar, mock_analytic_post): + with override_settings(**setting_map): + result = ship(temp_analytic_tar) + + assert result == expected_result + if expected_auth: + mock_analytic_post.assert_called_once() + assert mock_analytic_post.call_args[1]['auth'] == expected_auth + else: + mock_analytic_post.assert_not_called() + + +@pytest.mark.django_db +def test_gather_cleanup_on_auth_failure(mock_valid_license, temp_analytic_tar): + settings.INSIGHTS_TRACKING_STATE = True + settings.AUTOMATION_ANALYTICS_URL = 'https://example.com/api' + settings.REDHAT_USERNAME = 'test_user' + settings.REDHAT_PASSWORD = 'test_password' + + with tempfile.NamedTemporaryFile(delete=False, suffix='.tar.gz') as temp_file: + temp_file_path = temp_file.name + + try: + with mock.patch('awx.main.analytics.core.ship', return_value=False): + with mock.patch('awx.main.analytics.core.package', return_value=temp_file_path): + gather(module=importlib.import_module(__name__), collection_type='scheduled') + + assert not os.path.exists(temp_file_path), "Temp file was not cleaned up after ship failure" + finally: + if os.path.exists(temp_file_path): + os.remove(temp_file_path) diff --git a/awx/main/tests/functional/analytics/test_counts.py b/awx/main/tests/functional/analytics/test_counts.py index dd38c9ef3147..015467692d5b 100644 --- a/awx/main/tests/functional/analytics/test_counts.py +++ b/awx/main/tests/functional/analytics/test_counts.py @@ -66,7 +66,7 @@ def test_database_counts(organization_factory, job_template_factory, workflow_jo @pytest.mark.django_db def test_inventory_counts(organization_factory, inventory_factory): - (inv1, inv2, inv3) = [inventory_factory(f"inv-{i}") for i in range(3)] + inv1, inv2, inv3 = [inventory_factory(f"inv-{i}") for i in range(3)] s1 = inv1.inventory_sources.create(name="src1", source="ec2") s2 = inv1.inventory_sources.create(name="src2", source="file") diff --git a/awx/main/tests/functional/analytics/test_metrics.py b/awx/main/tests/functional/analytics/test_metrics.py index 6192d4e9bd94..2652db85b7c3 100644 --- a/awx/main/tests/functional/analytics/test_metrics.py +++ b/awx/main/tests/functional/analytics/test_metrics.py @@ -1,10 +1,12 @@ import pytest +from django.test import RequestFactory from prometheus_client.parser import text_string_to_metric_families +from rest_framework.request import Request from awx.main import models from awx.main.analytics.metrics import metrics +from awx.main.analytics.dispatcherd_metrics import get_dispatcherd_metrics from awx.api.versioning import reverse -from awx.main.models.rbac import Role EXPECTED_VALUES = { 'awx_system_info': 1.0, @@ -31,6 +33,7 @@ 'awx_license_instance_free': 0, 'awx_pending_jobs_total': 0, 'awx_database_connections_total': 1, + 'awx_license_expiry': 0, } @@ -49,7 +52,7 @@ def test_metrics_counts(organization_factory, job_template_factory, workflow_job for gauge in gauges: for sample in gauge.samples: # name, label, value, timestamp, exemplar - name, _, value, _, _ = sample + name, _, value, _, _, _ = sample assert EXPECTED_VALUES[name] == value @@ -66,7 +69,6 @@ def test_metrics_permissions(get, admin, org_admin, alice, bob, organization): organization.auditor_role.members.add(bob) assert get(get_metrics_view_db_only(), user=bob).status_code == 403 - Role.singleton('system_auditor').members.add(bob) bob.is_system_auditor = True assert get(get_metrics_view_db_only(), user=bob).status_code == 200 @@ -78,3 +80,55 @@ def test_metrics_http_methods(get, post, patch, put, options, admin): assert patch(get_metrics_view_db_only(), user=admin).status_code == 405 assert post(get_metrics_view_db_only(), user=admin).status_code == 405 assert options(get_metrics_view_db_only(), user=admin).status_code == 200 + + +class DummyMetricsResponse: + def __init__(self, payload): + self._payload = payload + + def read(self): + return self._payload + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + +def test_dispatcherd_metrics_node_filter_match(mocker, settings): + settings.CLUSTER_HOST_ID = "awx-1" + payload = b'# HELP test_metric A test metric\n# TYPE test_metric gauge\ntest_metric 1\n' + + def fake_urlopen(url, timeout=1.0): + return DummyMetricsResponse(payload) + + mocker.patch('urllib.request.urlopen', fake_urlopen) + + request = Request(RequestFactory().get('/api/v2/metrics/', {'node': 'awx-1'})) + + assert get_dispatcherd_metrics(request) == payload.decode('utf-8') + + +def test_dispatcherd_metrics_node_filter_excludes_local(mocker, settings): + settings.CLUSTER_HOST_ID = "awx-1" + + def fake_urlopen(*args, **kwargs): + raise AssertionError("urlopen should not be called when node filter excludes local node") + + mocker.patch('urllib.request.urlopen', fake_urlopen) + + request = Request(RequestFactory().get('/api/v2/metrics/', {'node': 'awx-2'})) + + assert get_dispatcherd_metrics(request) == '' + + +def test_dispatcherd_metrics_metric_filter_excludes_unrelated(mocker): + def fake_urlopen(*args, **kwargs): + raise AssertionError("urlopen should not be called when metric filter excludes dispatcherd metrics") + + mocker.patch('urllib.request.urlopen', fake_urlopen) + + request = Request(RequestFactory().get('/api/v2/metrics/', {'metric': 'awx_system_info'})) + + assert get_dispatcherd_metrics(request) == '' diff --git a/awx/main/tests/functional/api/test_activity_streams.py b/awx/main/tests/functional/api/test_activity_streams.py index 961fd02f8004..e66276b3cbf9 100644 --- a/awx/main/tests/functional/api/test_activity_streams.py +++ b/awx/main/tests/functional/api/test_activity_streams.py @@ -109,7 +109,8 @@ def test_stream_queryset_hides_shows_items( settings.ACTIVITY_STREAM_ENABLED = True # this user is not in any organizations and should not see any resource activity no_access_user = user('no-access-user', False) - queryset = ActivityStreamAccess(no_access_user).get_queryset() + access = ActivityStreamAccess(no_access_user) + queryset = access.get_queryset() assert not queryset.filter(project__pk=project.pk) assert not queryset.filter(credential__pk=org_credential.pk) @@ -120,9 +121,11 @@ def test_stream_queryset_hides_shows_items( assert not queryset.filter(host__pk=host.pk) assert not queryset.filter(team__pk=team.pk) assert not queryset.filter(notification_template__pk=notification_template.pk) + assert not access.can_read(activity_stream_entry) # Organization admin should be able to see most things in the ActivityStream - queryset = ActivityStreamAccess(org_admin).get_queryset() + access = ActivityStreamAccess(org_admin) + queryset = access.get_queryset() assert queryset.filter(project__pk=project.pk, operation='create').count() == 1 assert queryset.filter(credential__pk=org_credential.pk, operation='create').count() == 1 @@ -133,6 +136,7 @@ def test_stream_queryset_hides_shows_items( assert queryset.filter(host__pk=host.pk, operation='create').count() == 1 assert queryset.filter(team__pk=team.pk, operation='create').count() == 1 assert queryset.filter(notification_template__pk=notification_template.pk, operation='create').count() == 1 + assert access.can_read(activity_stream_entry) @pytest.mark.django_db diff --git a/awx/main/tests/functional/api/test_adhoc.py b/awx/main/tests/functional/api/test_adhoc.py index 983e45029c40..b6b0b7c7470b 100644 --- a/awx/main/tests/functional/api/test_adhoc.py +++ b/awx/main/tests/functional/api/test_adhoc.py @@ -3,7 +3,6 @@ from awx.api.versioning import reverse - """ def run_test_ad_hoc_command(self, **kwargs): # Post to list to start a new ad hoc command. diff --git a/awx/main/tests/functional/api/test_analytics.py b/awx/main/tests/functional/api/test_analytics.py new file mode 100644 index 000000000000..52fdba74e376 --- /dev/null +++ b/awx/main/tests/functional/api/test_analytics.py @@ -0,0 +1,259 @@ +import pytest +import requests +from unittest import mock +from awx.api.views.analytics import AnalyticsGenericView, MissingSettings, AUTOMATION_ANALYTICS_API_URL_PATH, ERROR_MISSING_USER, ERROR_MISSING_PASSWORD +from django.test.utils import override_settings +from django.test import RequestFactory +from rest_framework import status + +from awx.main.utils import get_awx_version +from django.utils import translation + + +class TestAnalyticsGenericView: + @pytest.mark.parametrize( + "existing_headers,expected_headers", + [ + ({}, {}), + ({'Hey': 'There'}, {}), # We don't forward just any headers + ({'Content-Type': 'text/html', 'Content-Length': '12'}, {'Content-Type': 'text/html', 'Content-Length': '12'}), + # Requests will auto-add the following headers (so we don't need to test them): 'Accept-Encoding', 'User-Agent', 'Accept' + ], + ) + def test__request_headers(self, existing_headers, expected_headers): + expected_headers['X-Rh-Analytics-Source'] = 'controller' + expected_headers['X-Rh-Analytics-Source-Version'] = get_awx_version() + expected_headers['Accept-Language'] = translation.get_language() + + request = requests.session() + request.headers.update(existing_headers) + assert set(expected_headers.items()).issubset(set(AnalyticsGenericView._request_headers(request).items())) + + @pytest.mark.parametrize( + "path,expected_path", + [ + ('A/B', f'{AUTOMATION_ANALYTICS_API_URL_PATH}/A/B'), + ('B', f'{AUTOMATION_ANALYTICS_API_URL_PATH}/B'), + ('/a/b/c/analytics/reports/my_slug', f'{AUTOMATION_ANALYTICS_API_URL_PATH}/reports/my_slug'), + ('/a/b/c/analytics/', f'{AUTOMATION_ANALYTICS_API_URL_PATH}/'), + ('/a/b/c/analytics', f'{AUTOMATION_ANALYTICS_API_URL_PATH}//a/b/c/analytics'), # Because there is no ending / on analytics we get a weird condition + ('/a/b/c/analytics/', f'{AUTOMATION_ANALYTICS_API_URL_PATH}/'), + ], + ) + @pytest.mark.django_db + def test__get_analytics_path(self, path, expected_path): + assert AnalyticsGenericView._get_analytics_path(path) == expected_path + + @pytest.mark.django_db + def test__get_analytics_url_no_url(self): + with override_settings(AUTOMATION_ANALYTICS_URL=None): + with pytest.raises(MissingSettings): + agw = AnalyticsGenericView() + agw._get_analytics_url('A') + + @pytest.mark.parametrize( + "request_path,ending_url", + [ + ('A', 'A'), + ('A/B', 'A/B'), + ('A/B/analytics/', ''), # we split on analytics but because there is nothing after + ('A/B/analytics/report', 'report'), + ('A/B/analytics/report/slug', 'report/slug'), + ], + ) + @pytest.mark.django_db + def test__get_analytics_url(self, request_path, ending_url): + base_url = 'http://testing' + with override_settings(AUTOMATION_ANALYTICS_URL=base_url): + agw = AnalyticsGenericView() + assert agw._get_analytics_url(request_path) == f'{base_url}{AUTOMATION_ANALYTICS_API_URL_PATH}/{ending_url}' + + @pytest.mark.parametrize( + "setting_name,setting_value,raises", + [ + ('INSIGHTS_TRACKING_STATE', None, True), + ('INSIGHTS_TRACKING_STATE', False, True), + ('INSIGHTS_TRACKING_STATE', True, False), + ('INSIGHTS_TRACKING_STATE', 'Steve', False), + ('INSIGHTS_TRACKING_STATE', 1, False), + ('INSIGHTS_TRACKING_STATE', '', True), + ], + ) + @pytest.mark.django_db + def test__get_setting(self, setting_name, setting_value, raises): + with override_settings(**{setting_name: setting_value}): + if raises: + with pytest.raises(MissingSettings): + AnalyticsGenericView._get_setting(setting_name, False, None) + else: + assert AnalyticsGenericView._get_setting(setting_name, False, None) == setting_value + + @pytest.mark.parametrize( + "settings_map, expected_auth, expected_error_keyword", + [ + # Test case 1: Valid Red Hat credentials + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': 'redhat_user', + 'REDHAT_PASSWORD': 'redhat_pass', # NOSONAR + 'SUBSCRIPTIONS_CLIENT_ID': '', + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + ('redhat_user', 'redhat_pass'), + None, + ), + # Test case 2: Valid Subscription credentials + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': '', + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', + 'SUBSCRIPTIONS_CLIENT_SECRET': 'subs_pass', # NOSONAR + }, + ('subs_user', 'subs_pass'), + None, + ), + # Test case 3: No credentials + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': '', + 'SUBSCRIPTIONS_CLIENT_ID': '', + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + None, + ERROR_MISSING_USER, + ), + # Test case 4: Both credentials + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': 'redhat_user', + 'REDHAT_PASSWORD': 'redhat_pass', # NOSONAR + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', + 'SUBSCRIPTIONS_CLIENT_SECRET': 'subs_pass', # NOSONAR + }, + ('redhat_user', 'redhat_pass'), + None, + ), + # Test case 5: Missing password + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': '', + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', # NOSONAR + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + None, + ERROR_MISSING_PASSWORD, + ), + ], + ) + @pytest.mark.django_db + def test__send_to_analytics_credentials(self, settings_map, expected_auth, expected_error_keyword): + """ + Test _send_to_analytics with various combinations of credentials. + """ + with override_settings(**settings_map): + request = RequestFactory().post('/some/path') + view = AnalyticsGenericView() + + if expected_auth: + with mock.patch('awx.api.views.analytics.OIDCClient') as mock_oidc_client: + # Configure the mock OIDCClient instance and its make_request method + mock_client_instance = mock.Mock() + mock_oidc_client.return_value = mock_client_instance + mock_client_instance.make_request.return_value = mock.Mock(status_code=200) + + analytic_url = view._get_analytics_url(request.path) + response = view._send_to_analytics(request, 'POST') + + # Assertions + # Assert OIDCClient instantiation + expected_client_id, expected_client_secret = expected_auth + mock_oidc_client.assert_called_once_with(expected_client_id, expected_client_secret) + + # Assert make_request call + mock_client_instance.make_request.assert_called_once_with( + 'POST', + analytic_url, + headers=mock.ANY, + verify=mock.ANY, + params=mock.ANY, + json=mock.ANY, + timeout=mock.ANY, + ) + assert response.status_code == 200 + else: + # Test when settings are missing and MissingSettings is raised + response = view._send_to_analytics(request, 'POST') + + # # Assert that _error_response is called when MissingSettings is raised + # mock_error_response.assert_called_once_with(expected_error_keyword, remote=False) + assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.data['error']['keyword'] == expected_error_keyword + + @pytest.mark.django_db + @pytest.mark.parametrize( + "settings_map, expected_auth", + [ + # Test case 1: Username and password should be used for basic auth + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': 'redhat_user', + 'REDHAT_PASSWORD': 'redhat_pass', # NOSONAR + 'SUBSCRIPTIONS_CLIENT_ID': '', + 'SUBSCRIPTIONS_CLIENT_SECRET': '', + }, + ('redhat_user', 'redhat_pass'), + ), + # Test case 2: Client ID and secret should be used for basic auth + ( + { + 'INSIGHTS_TRACKING_STATE': True, + 'REDHAT_USERNAME': '', + 'REDHAT_PASSWORD': '', + 'SUBSCRIPTIONS_CLIENT_ID': 'subs_user', + 'SUBSCRIPTIONS_CLIENT_SECRET': 'subs_pass', # NOSONAR + }, + None, + ), + ], + ) + def test__send_to_analytics_fallback_to_basic_auth(self, settings_map, expected_auth): + """ + Test _send_to_analytics with basic auth fallback. + """ + with override_settings(**settings_map): + request = RequestFactory().post('/some/path') + view = AnalyticsGenericView() + + with mock.patch('awx.api.views.analytics.OIDCClient') as mock_oidc_client, mock.patch( + 'awx.api.views.analytics.AnalyticsGenericView._base_auth_request' + ) as mock_base_auth_request: + # Configure the mock OIDCClient instance and its make_request method + mock_client_instance = mock.Mock() + mock_oidc_client.return_value = mock_client_instance + mock_client_instance.make_request.side_effect = requests.RequestException("Incorrect credentials") + + analytic_url = view._get_analytics_url(request.path) + view._send_to_analytics(request, 'POST') + + if expected_auth: + # assert mock_base_auth_request called with expected_auth + mock_base_auth_request.assert_called_once_with( + request, + 'POST', + analytic_url, + expected_auth[0], + expected_auth[1], + mock.ANY, + ) + else: + # assert mock_base_auth_request not called + mock_base_auth_request.assert_not_called() diff --git a/awx/main/tests/functional/test_api_generics.py b/awx/main/tests/functional/api/test_api_generics.py similarity index 100% rename from awx/main/tests/functional/test_api_generics.py rename to awx/main/tests/functional/api/test_api_generics.py diff --git a/awx/main/tests/functional/api/test_application_name.py b/awx/main/tests/functional/api/test_application_name.py new file mode 100644 index 000000000000..da7cda210ca0 --- /dev/null +++ b/awx/main/tests/functional/api/test_application_name.py @@ -0,0 +1,23 @@ +import pytest +from awx.settings.application_name import get_service_name, set_application_name + + +@pytest.mark.parametrize( + 'argv,result', + ( + ([], None), + (['-m'], None), + (['-m', 'python'], None), + (['-m', 'python', 'manage'], None), + (['-m', 'python', 'manage', 'a'], 'a'), + (['-m', 'python', 'manage', 'b', 'a'], 'b'), + (['-m', 'python', 'manage', 'run_something', 'b', 'a'], 'something'), + ), +) +def test_get_service_name(argv, result): + assert get_service_name(argv) == result + + +@pytest.mark.parametrize('DATABASES,CLUSTER_ID,function', (({}, 12, ''), ({'default': {'ENGINE': 'sqllite3'}}, 12, ''))) +def test_set_application_name(DATABASES, CLUSTER_ID, function): + set_application_name(DATABASES, CLUSTER_ID, function) diff --git a/awx/main/tests/functional/api/test_auth.py b/awx/main/tests/functional/api/test_auth.py index d9ac588de323..49a9c7640df2 100644 --- a/awx/main/tests/functional/api/test_auth.py +++ b/awx/main/tests/functional/api/test_auth.py @@ -1,12 +1,16 @@ import pytest from django.contrib import auth +from django.http import JsonResponse + from django.test import Client from rest_framework.test import APIRequestFactory -from awx.api.generics import LoggedLoginView -from awx.api.versioning import drf_reverse +import awx.api.generics +from rest_framework.reverse import reverse as drf_reverse + +from pytest_mock import MockerFixture @pytest.mark.django_db @@ -21,6 +25,25 @@ def test_invalid_login(): request = factory.post(url, data) request.user = anon - response = LoggedLoginView.as_view()(request) + response = awx.api.generics.LoggedLoginView.as_view()(request) + + assert response.status_code == 401 + +@pytest.mark.django_db +def test_invalid_post(mocker: MockerFixture, monkeypatch: pytest.MonkeyPatch): + url = drf_reverse('api:login') + factory = APIRequestFactory() + request = factory.post(url) + + is_proxied_request_mock = mocker.Mock( + autospec=True, + name='is_proxied_request', + return_value=True, + ) + monkeypatch.setattr(awx.api.generics, 'is_proxied_request', is_proxied_request_mock) + response = awx.api.generics.LoggedLoginView.as_view()(request) + + assert isinstance(response, JsonResponse) + assert b'Please log in via Platform Authentication.' in response.content assert response.status_code == 401 diff --git a/awx/main/tests/functional/api/test_create_attach_views.py b/awx/main/tests/functional/api/test_create_attach_views.py index b22ec089122f..7b92f82f5076 100644 --- a/awx/main/tests/functional/api/test_create_attach_views.py +++ b/awx/main/tests/functional/api/test_create_attach_views.py @@ -9,8 +9,8 @@ def test_user_role_view_access(rando, inventory, mocker, post): role_pk = inventory.admin_role.pk data = {"id": role_pk} mock_access = mocker.MagicMock(can_attach=mocker.MagicMock(return_value=False)) - with mocker.patch('awx.main.access.RoleAccess', return_value=mock_access): - post(url=reverse('api:user_roles_list', kwargs={'pk': rando.pk}), data=data, user=rando, expect=403) + mocker.patch('awx.main.access.RoleAccess', return_value=mock_access) + post(url=reverse('api:user_roles_list', kwargs={'pk': rando.pk}), data=data, user=rando, expect=403) mock_access.can_attach.assert_called_once_with(inventory.admin_role, rando, 'members', data, skip_sub_obj_read_check=False) @@ -21,8 +21,8 @@ def test_team_role_view_access(rando, team, inventory, mocker, post): role_pk = inventory.admin_role.pk data = {"id": role_pk} mock_access = mocker.MagicMock(can_attach=mocker.MagicMock(return_value=False)) - with mocker.patch('awx.main.access.RoleAccess', return_value=mock_access): - post(url=reverse('api:team_roles_list', kwargs={'pk': team.pk}), data=data, user=rando, expect=403) + mocker.patch('awx.main.access.RoleAccess', return_value=mock_access) + post(url=reverse('api:team_roles_list', kwargs={'pk': team.pk}), data=data, user=rando, expect=403) mock_access.can_attach.assert_called_once_with(inventory.admin_role, team, 'member_role.parents', data, skip_sub_obj_read_check=False) @@ -33,8 +33,8 @@ def test_role_team_view_access(rando, team, inventory, mocker, post): role_pk = inventory.admin_role.pk data = {"id": team.pk} mock_access = mocker.MagicMock(return_value=False, __name__='mocked') - with mocker.patch('awx.main.access.RoleAccess.can_attach', mock_access): - post(url=reverse('api:role_teams_list', kwargs={'pk': role_pk}), data=data, user=rando, expect=403) + mocker.patch('awx.main.access.RoleAccess.can_attach', mock_access) + post(url=reverse('api:role_teams_list', kwargs={'pk': role_pk}), data=data, user=rando, expect=403) mock_access.assert_called_once_with(inventory.admin_role, team, 'member_role.parents', data, skip_sub_obj_read_check=False) diff --git a/awx/main/tests/functional/api/test_credential.py b/awx/main/tests/functional/api/test_credential.py index 52814f3655ea..7956ebed4fc1 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -12,25 +12,13 @@ EXAMPLE_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\nxyz==\n-----END PRIVATE KEY-----' EXAMPLE_ENCRYPTED_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nxyz==\n-----END PRIVATE KEY-----' - -@pytest.mark.django_db -def test_idempotent_credential_type_setup(): - assert CredentialType.objects.count() == 0 - CredentialType.setup_tower_managed_defaults() - total = CredentialType.objects.count() - assert total > 0 - - CredentialType.setup_tower_managed_defaults() - assert CredentialType.objects.count() == total - - # # user credential creation # @pytest.mark.django_db -def test_create_user_credential_via_credentials_list(post, get, alice, credentialtype_ssh): +def test_create_user_credential_via_credentials_list(post, get, alice, credentialtype_ssh, setup_managed_roles): params = { 'credential_type': 1, 'inputs': {'username': 'someusername'}, @@ -77,11 +65,11 @@ def test_credential_validation_error_with_multiple_owner_fields(post, admin, ali } response = post(reverse('api:credential_list'), params, admin) assert response.status_code == 400 - assert response.data['detail'][0] == ("Only one of 'user', 'team', or 'organization' should be provided, " "received organization, team, user fields.") + assert response.data['detail'][0] == ("Only one of 'user', 'team', or 'organization' should be provided, received organization, team, user fields.") @pytest.mark.django_db -def test_create_user_credential_via_user_credentials_list(post, get, alice, credentialtype_ssh): +def test_create_user_credential_via_user_credentials_list(post, get, alice, credentialtype_ssh, setup_managed_roles): params = { 'credential_type': 1, 'inputs': {'username': 'someusername'}, @@ -299,6 +287,72 @@ def test_sa_grant_private_credential_to_team_through_role_teams(post, credential assert response.status_code == 400 +@pytest.mark.django_db +def test_grant_credential_to_team_different_organization_through_role_teams(post, get, credential, organizations, admin, org_admin, team, team_member): + # # Test that credential from different org can be assigned to team by a superuser through role_teams_list endpoint + orgs = organizations(2) + credential.organization = orgs[0] + credential.save() + team.organization = orgs[1] + team.save() + + # Non-superuser (org_admin) trying cross-org assignment should be denied + response = post(reverse('api:role_teams_list', kwargs={'pk': credential.use_role.id}), {'id': team.id}, org_admin) + assert response.status_code == 400 + assert ( + "You cannot grant a team access to a credential in a different organization. Only superusers can grant cross-organization credential access to teams" + in response.data['msg'] + ) + + # Superuser (admin) can do cross-org assignment + response = post(reverse('api:role_teams_list', kwargs={'pk': credential.use_role.id}), {'id': team.id}, admin) + assert response.status_code == 204 + + assert credential.use_role in team.member_role.children.all() + assert team_member in credential.read_role + assert team_member in credential.use_role + assert team_member not in credential.admin_role + + +@pytest.mark.django_db +def test_grant_credential_to_team_different_organization(post, get, credential, organizations, admin, org_admin, team, team_member): + # Test that credential from different org can be assigned to team by a superuser + orgs = organizations(2) + credential.organization = orgs[0] + credential.save() + team.organization = orgs[1] + team.save() + + # Non-superuser (org_admin, ...) trying cross-org assignment should be denied + response = post(reverse('api:team_roles_list', kwargs={'pk': team.id}), {'id': credential.use_role.id}, org_admin) + assert response.status_code == 400 + assert ( + "You cannot grant a team access to a credential in a different organization. Only superusers can grant cross-organization credential access to teams" + in response.data['msg'] + ) + + # Superuser (system admin) can do cross-org assignment + response = post(reverse('api:team_roles_list', kwargs={'pk': team.id}), {'id': credential.use_role.id}, admin) + assert response.status_code == 204 + + assert credential.use_role in team.member_role.children.all() + + assert team_member in credential.read_role + assert team_member in credential.use_role + assert team_member not in credential.admin_role + + # Team member can see the credential in API + response = get(reverse('api:team_credentials_list', kwargs={'pk': team.id}), team_member) + assert response.status_code == 200 + assert response.data['count'] == 1 + assert response.data['results'][0]['id'] == credential.id + + # Team member can see the credential in general credentials API + response = get(reverse('api:credential_list'), team_member) + assert response.status_code == 200 + assert any(cred['id'] == credential.id for cred in response.data['results']) + + @pytest.mark.django_db def test_sa_grant_private_credential_to_team_through_team_roles(post, credential, admin, team): # not even a system admin can grant a private cred to a team though @@ -385,10 +439,9 @@ def test_list_created_org_credentials(post, get, organization, org_admin, org_me @pytest.mark.django_db def test_list_cannot_order_by_encrypted_field(post, get, organization, org_admin, credentialtype_ssh, order_by): for i, password in enumerate(('abc', 'def', 'xyz')): - response = post(reverse('api:credential_list'), {'organization': organization.id, 'name': 'C%d' % i, 'password': password}, org_admin) + post(reverse('api:credential_list'), {'organization': organization.id, 'name': 'C%d' % i, 'password': password}, org_admin, expect=400) - response = get(reverse('api:credential_list'), org_admin, QUERY_STRING='order_by=%s' % order_by, status=400) - assert response.status_code == 400 + get(reverse('api:credential_list'), org_admin, QUERY_STRING='order_by=%s' % order_by, expect=400) @pytest.mark.django_db @@ -399,8 +452,7 @@ def test_inputs_cannot_contain_extra_fields(get, post, organization, admin, cred 'credential_type': credentialtype_ssh.pk, 'inputs': {'invalid_field': 'foo'}, } - response = post(reverse('api:credential_list'), params, admin) - assert response.status_code == 400 + response = post(reverse('api:credential_list'), params, admin, expect=400) assert "'invalid_field' was unexpected" in response.data['inputs'][0] @@ -925,7 +977,7 @@ def _change_credential_type(): response = _change_credential_type() assert response.status_code == 400 - expected = ['You cannot change the credential type of the credential, ' 'as it may break the functionality of the resources using it.'] + expected = ['You cannot change the credential type of the credential, as it may break the functionality of the resources using it.'] assert response.data['credential_type'] == expected response = patch(reverse('api:credential_detail', kwargs={'pk': cred.pk}), {'name': 'Worst credential ever'}, admin) @@ -962,7 +1014,7 @@ def _change_credential_type(): response = _change_credential_type() assert response.status_code == 400 - expected = ['You cannot change the credential type of the credential, ' 'as it may break the functionality of the resources using it.'] + expected = ['You cannot change the credential type of the credential, as it may break the functionality of the resources using it.'] assert response.data['credential_type'] == expected response = patch(reverse('api:credential_detail', kwargs={'pk': cred.pk}), {'name': 'Worst credential ever'}, admin) @@ -994,7 +1046,7 @@ def _change_credential_type(): response = _change_credential_type() assert response.status_code == 400 - expected = ['You cannot change the credential type of the credential, ' 'as it may break the functionality of the resources using it.'] + expected = ['You cannot change the credential type of the credential, as it may break the functionality of the resources using it.'] assert response.data['credential_type'] == expected response = patch(reverse('api:credential_detail', kwargs={'pk': cred.pk}), {'name': 'Worst credential ever'}, admin) @@ -1238,6 +1290,30 @@ def test_custom_credential_type_create(get, post, organization, admin): assert decrypt_field(cred, 'api_token') == 'secret' +@pytest.mark.django_db +def test_galaxy_create_ok(post, organization, admin): + params = { + 'credential_type': 1, + 'name': 'Galaxy credential', + 'inputs': { + 'url': 'https://galaxy.ansible.com', + 'token': 'some_galaxy_token', + }, + } + galaxy = CredentialType.defaults['galaxy_api_token']() + galaxy.save() + params['user'] = admin.id + params['credential_type'] = galaxy.pk + response = post(reverse('api:credential_list'), params, admin) + assert response.status_code == 201 + + assert Credential.objects.count() == 1 + cred = Credential.objects.all()[:1].get() + assert cred.credential_type == galaxy + assert cred.inputs['url'] == 'https://galaxy.ansible.com' + assert decrypt_field(cred, 'token') == 'some_galaxy_token' + + # # misc xfail conditions # diff --git a/awx/main/tests/functional/api/test_credential_input_sources.py b/awx/main/tests/functional/api/test_credential_input_sources.py index d13710e0ab36..3db2c3ac9c59 100644 --- a/awx/main/tests/functional/api/test_credential_input_sources.py +++ b/awx/main/tests/functional/api/test_credential_input_sources.py @@ -1,5 +1,7 @@ import pytest +from ansible_base.lib.testing.util import feature_flag_enabled, feature_flag_disabled + from awx.main.models import CredentialInputSource from awx.api.versioning import reverse @@ -316,3 +318,60 @@ def test_create_credential_input_source_with_already_used_input_returns_400(post ] all_responses = [post(list_url, params, admin) for params in all_params] assert all_responses.pop().status_code == 400 + + +@pytest.mark.django_db +def test_credential_input_source_passes_workload_identity_token_when_flag_enabled(vault_credential, external_credential, mocker): + """Test that workload_identity_token is passed to backend when flag is enabled.""" + with feature_flag_enabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + # Add workload_identity_token as an internal field on the external credential type + # so get_input_value resolves it from the per-input-source context + external_credential.credential_type.inputs['fields'].append( + {'id': 'workload_identity_token', 'label': 'Workload Identity Token', 'type': 'string', 'internal': True} + ) + + # Create an input source + input_source = CredentialInputSource.objects.create( + target_credential=vault_credential, + source_credential=external_credential, + input_field_name='vault_password', + metadata={'key': 'test_key'}, + ) + + # Mock the credential plugin backend + mock_backend = mocker.patch.object(external_credential.credential_type.plugin, 'backend', autospec=True, return_value='test_value') + + # Call with context keyed by input source PK + test_context = {input_source.pk: {'workload_identity_token': 'jwt_token_here'}} + result = input_source.get_input_value(context=test_context) + + # Verify backend was called with workload_identity_token + assert result == 'test_value' + call_kwargs = mock_backend.call_args[1] + assert call_kwargs['workload_identity_token'] == 'jwt_token_here' + assert call_kwargs['key'] == 'test_key' + + +@pytest.mark.django_db +def test_credential_input_source_skips_workload_identity_token_when_flag_disabled(vault_credential, external_credential, mocker): + """Test that workload_identity_token is NOT passed when flag is disabled.""" + with feature_flag_disabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + # Create an input source + input_source = CredentialInputSource.objects.create( + target_credential=vault_credential, + source_credential=external_credential, + input_field_name='vault_password', + metadata={'key': 'test_key'}, + ) + # Mock the credential plugin backend + mock_backend = mocker.patch.object(external_credential.credential_type.plugin, 'backend', autospec=True, return_value='test_value') + # Call with context containing workload_identity_token but NO internal field defined, + # simulating a flag-disabled scenario where tokens are not generated upstream + test_context = {input_source.pk: {'workload_identity_token': 'jwt_token_here'}} + result = input_source.get_input_value(context=test_context) + # Verify backend was called WITHOUT workload_identity_token since the credential type + # does not define it as an internal field (flag-disabled path doesn't register it) + assert result == 'test_value' + call_kwargs = mock_backend.call_args[1] + assert 'workload_identity_token' not in call_kwargs + assert call_kwargs['key'] == 'test_key' diff --git a/awx/main/tests/functional/api/test_deprecated_credential_assignment.py b/awx/main/tests/functional/api/test_deprecated_credential_assignment.py index 266218c0cb23..02937c184514 100644 --- a/awx/main/tests/functional/api/test_deprecated_credential_assignment.py +++ b/awx/main/tests/functional/api/test_deprecated_credential_assignment.py @@ -6,12 +6,6 @@ from awx.api.versioning import reverse -@pytest.fixture -def ec2_source(inventory, project): - with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): - return inventory.inventory_sources.create(name='some_source', source='ec2', source_project=project) - - @pytest.fixture def job_template(job_template, project, inventory): job_template.playbook = 'helloworld.yml' diff --git a/awx/main/tests/functional/api/test_events.py b/awx/main/tests/functional/api/test_events.py index 34ecf4d691cc..ebc8900cae84 100644 --- a/awx/main/tests/functional/api/test_events.py +++ b/awx/main/tests/functional/api/test_events.py @@ -73,6 +73,7 @@ def test_job_job_events_children_summary(get, organization_factory, job_template job_id=job.pk, uuid='uuid3', parent_uuid='uuid2', event="playbook_on_task_start", counter=3, stdout='a' * 1024, job_created=job.created ).save() JobEvent.create_from_data(job_id=job.pk, uuid='uuid4', parent_uuid='', event='verbose', counter=4, stdout='a' * 1024, job_created=job.created).save() + JobEvent.create_from_data( job_id=job.pk, uuid='uuid5', parent_uuid='uuid1', event="playbook_on_play_start", counter=5, stdout='a' * 1024, job_created=job.created ).save() @@ -131,3 +132,46 @@ def test_job_job_events_children_summary_is_tree(get, organization_factory, job_ assert response.data["meta_event_nested_uuid"] == {} assert response.data["event_processing_finished"] == True assert response.data["is_tree"] == False + + +@pytest.mark.django_db +def test_job_job_events_children_summary_empty_event(get, organization_factory, job_template_factory): + objs = organization_factory("org", superusers=['admin']) + jt = job_template_factory("jt", organization=objs.organization, inventory='test_inv', project='test_proj').job_template + job = jt.create_unified_job() + url = reverse('api:job_job_events_children_summary', kwargs={'pk': job.pk}) + response = get(url, user=objs.superusers.admin, expect=200) + assert response.data["event_processing_finished"] == False + ''' + E1 + E2 + E3 + E4 (verbose) + E5 + ''' + JobEvent.create_from_data( + job_id=job.pk, uuid='uuid1', parent_uuid='', event="playbook_on_start", counter=1, stdout='a' * 1024, job_created=job.created + ).save() + JobEvent.create_from_data( + job_id=job.pk, uuid='uuid2', parent_uuid='uuid1', event="playbook_on_play_start", counter=2, stdout='a' * 1024, job_created=job.created + ).save() + JobEvent.create_from_data( + job_id=job.pk, uuid='uuid3', parent_uuid='uuid2', event="playbook_on_task_start", counter=3, stdout='a' * 1024, job_created=job.created + ).save() + JobEvent.create_from_data(job_id=job.pk, uuid='uuid4', parent_uuid='', event='verbose', counter=4, stdout='a' * 1024, job_created=job.created).save() + + JobEvent.create_from_data(job_id=job.pk, uuid='uuid4', parent_uuid='', event='', counter=5, stdout='a' * 1024, job_created=job.created).save() + + JobEvent.create_from_data( + job_id=job.pk, uuid='uuid5', parent_uuid='uuid1', event="playbook_on_play_start", counter=6, stdout='a' * 1024, job_created=job.created + ).save() + + job.emitted_events = job.get_event_queryset().count() + job.status = "successful" + job.save() + url = reverse('api:job_job_events_children_summary', kwargs={'pk': job.pk}) + response = get(url, user=objs.superusers.admin, expect=200) + assert response.data["children_summary"] == {1: {"rowNumber": 0, "numChildren": 4}, 2: {"rowNumber": 1, "numChildren": 2}} + assert response.data["meta_event_nested_uuid"] == {4: "uuid2"} + assert response.data["event_processing_finished"] == True + assert response.data["is_tree"] == True diff --git a/awx/main/tests/functional/api/test_generic.py b/awx/main/tests/functional/api/test_generic.py index 0c064f23a40c..87571c8eeabe 100644 --- a/awx/main/tests/functional/api/test_generic.py +++ b/awx/main/tests/functional/api/test_generic.py @@ -1,22 +1,30 @@ import pytest +from unittest import mock from awx.api.versioning import reverse +from django.test.utils import override_settings + +from ansible_base.jwt_consumer.common.util import generate_x_trusted_proxy_header +from ansible_base.lib.testing.fixtures import rsa_keypair_factory, rsa_keypair # noqa: F401; pylint: disable=unused-import + + +class HeaderTrackingMiddleware(object): + def __init__(self): + self.environ = {} + + def process_request(self, request): + pass + + def process_response(self, request, response): + self.environ = request.environ + @pytest.mark.django_db def test_proxy_ip_allowed(get, patch, admin): url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'system'}) patch(url, user=admin, data={'REMOTE_HOST_HEADERS': ['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST']}) - class HeaderTrackingMiddleware(object): - environ = {} - - def process_request(self, request): - pass - - def process_response(self, request, response): - self.environ = request.environ - # By default, `PROXY_IP_ALLOWED_LIST` is disabled, so custom `REMOTE_HOST_HEADERS` # should just pass through middleware = HeaderTrackingMiddleware() @@ -45,6 +53,51 @@ def process_response(self, request, response): assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip' +@pytest.mark.django_db +class TestTrustedProxyAllowListIntegration: + @pytest.fixture + def url(self, patch, admin): + url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'system'}) + patch(url, user=admin, data={'REMOTE_HOST_HEADERS': ['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST']}) + patch(url, user=admin, data={'PROXY_IP_ALLOWED_LIST': ['my.proxy.example.org']}) + return url + + @pytest.fixture + def middleware(self): + return HeaderTrackingMiddleware() + + def test_x_trusted_proxy_valid_signature(self, get, admin, rsa_keypair, url, middleware): # noqa: F811 + # Headers should NOT get deleted + headers = { + 'HTTP_X_TRUSTED_PROXY': generate_x_trusted_proxy_header(rsa_keypair.private), + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'some-actual-ip', + } + with mock.patch('ansible_base.jwt_consumer.common.cache.JWTCache.get_key_from_cache', lambda self: None): + with override_settings(ANSIBLE_BASE_JWT_KEY=rsa_keypair.public, PROXY_IP_ALLOWED_LIST=[]): + get(url, user=admin, middleware=middleware, **headers) + assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip' + + def test_x_trusted_proxy_invalid_signature(self, get, admin, url, patch, middleware): + # Headers should NOT get deleted + headers = { + 'HTTP_X_TRUSTED_PROXY': 'DEAD-BEEF', + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'some-actual-ip', + } + with override_settings(PROXY_IP_ALLOWED_LIST=[]): + get(url, user=admin, middleware=middleware, **headers) + assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip' + + def test_x_trusted_proxy_invalid_signature_valid_proxy(self, get, admin, url, middleware): + # A valid explicit proxy SHOULD result in sensitive headers NOT being deleted, regardless of the trusted proxy signature results + headers = { + 'HTTP_X_TRUSTED_PROXY': 'DEAD-BEEF', + 'REMOTE_ADDR': 'my.proxy.example.org', + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'some-actual-ip', + } + get(url, user=admin, middleware=middleware, **headers) + assert middleware.environ['HTTP_X_FROM_THE_LOAD_BALANCER'] == 'some-actual-ip' + + @pytest.mark.django_db class TestDeleteViews: def test_sublist_delete_permission_check(self, inventory_source, host, rando, delete): diff --git a/awx/main/tests/functional/api/test_instance.py b/awx/main/tests/functional/api/test_instance.py index b9ec4d2ab088..c2e18b765dae 100644 --- a/awx/main/tests/functional/api/test_instance.py +++ b/awx/main/tests/functional/api/test_instance.py @@ -1,3 +1,5 @@ +from unittest import mock + import pytest from awx.api.versioning import reverse @@ -5,7 +7,9 @@ from awx.main.models.ha import Instance from django.test.utils import override_settings +from django.http import HttpResponse +from rest_framework import status INSTANCE_KWARGS = dict(hostname='example-host', cpu=6, node_type='execution', memory=36000000000, cpu_capacity=6, mem_capacity=42) @@ -84,5 +88,14 @@ def test_custom_hostname_regex(post, admin_user): "hostname": value[0], "node_type": "execution", "node_state": "installed", + "peers": [], } post(url=url, user=admin_user, data=data, expect=value[1]) + + +def test_instance_install_bundle(get, admin_user, system_auditor): + instance = Instance.objects.create(**INSTANCE_KWARGS) + url = reverse('api:instance_install_bundle', kwargs={'pk': instance.pk}) + with mock.patch('awx.api.views.instance_install_bundle.InstanceInstallBundle.get', return_value=HttpResponse({'test': 'data'}, status=status.HTTP_200_OK)): + get(url=url, user=admin_user, expect=200) + get(url=url, user=system_auditor, expect=403) diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py index aa8204c6dae9..5bc56940c980 100644 --- a/awx/main/tests/functional/api/test_instance_group.py +++ b/awx/main/tests/functional/api/test_instance_group.py @@ -32,13 +32,6 @@ def fn(hostname, node_type): return fn -@pytest.fixture -def instance_group(job_factory): - ig = InstanceGroup(name="east") - ig.save() - return ig - - @pytest.fixture def containerized_instance_group(instance_group, kube_credential): ig = InstanceGroup(name="container") diff --git a/awx/main/tests/functional/api/test_instance_peers.py b/awx/main/tests/functional/api/test_instance_peers.py new file mode 100644 index 000000000000..1ce6f843bd78 --- /dev/null +++ b/awx/main/tests/functional/api/test_instance_peers.py @@ -0,0 +1,608 @@ +import pytest +import yaml +from unittest import mock + +from awx.api.versioning import reverse +from awx.main.models import Instance, ReceptorAddress +from awx.api.views.instance_install_bundle import generate_group_vars_all_yml + + +def has_peer(group_vars, peer): + peers = group_vars.get('receptor_peers', []) + for p in peers: + if p['address'] == peer: + return True + return False + + +@pytest.mark.django_db +class TestPeers: + @pytest.fixture(autouse=True) + def configure_settings(self, settings): + settings.IS_K8S = True + + @pytest.mark.parametrize('node_type', ['hop', 'execution']) + def test_peering_to_self(self, node_type, admin_user, patch): + """ + cannot peer to self + """ + instance = Instance.objects.create(hostname='abc', node_type=node_type) + addr = ReceptorAddress.objects.create(instance=instance, address='abc', canonical=True) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': instance.pk}), + data={"hostname": "abc", "node_type": node_type, "peers": [addr.id]}, + user=admin_user, + expect=400, + ) + assert 'Instance cannot peer to its own address.' in str(resp.data) + + @pytest.mark.parametrize('node_type', ['control', 'hybrid', 'hop', 'execution']) + def test_creating_node(self, node_type, admin_user, post): + """ + can only add hop and execution nodes via API + """ + resp = post( + url=reverse('api:instance_list'), + data={"hostname": "abc", "node_type": node_type}, + user=admin_user, + expect=400 if node_type in ['control', 'hybrid'] else 201, + ) + if resp.status_code == 400: + assert 'Can only create execution or hop nodes.' in str(resp.data) + + def test_changing_node_type(self, admin_user, patch): + """ + cannot change node type + """ + hop = Instance.objects.create(hostname='abc', node_type="hop") + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"node_type": "execution"}, + user=admin_user, + expect=400, + ) + assert 'Cannot change node type.' in str(resp.data) + + @pytest.mark.parametrize( + 'payload_port, payload_peers_from, initial_port, initial_peers_from', + [ + (-1, -1, None, None), + (-1, -1, 27199, False), + (-1, -1, 27199, True), + (None, -1, None, None), + (None, False, None, None), + (-1, False, None, None), + (27199, True, 27199, True), + (27199, False, 27199, False), + (27199, -1, 27199, True), + (27199, -1, 27199, False), + (-1, True, 27199, True), + (-1, False, 27199, False), + ], + ) + def test_no_op(self, payload_port, payload_peers_from, initial_port, initial_peers_from, admin_user, patch): + node = Instance.objects.create(hostname='abc', node_type='hop') + if initial_port is not None: + ReceptorAddress.objects.create(address=node.hostname, port=initial_port, canonical=True, peers_from_control_nodes=initial_peers_from, instance=node) + + assert ReceptorAddress.objects.filter(instance=node).count() == 1 + else: + assert ReceptorAddress.objects.filter(instance=node).count() == 0 + + data = {'enabled': True} # Just to have something to post. + if payload_port != -1: + data['listener_port'] = payload_port + if payload_peers_from != -1: + data['peers_from_control_nodes'] = payload_peers_from + + patch( + url=reverse('api:instance_detail', kwargs={'pk': node.pk}), + data=data, + user=admin_user, + expect=200, + ) + + assert ReceptorAddress.objects.filter(instance=node).count() == (0 if initial_port is None else 1) + if initial_port is not None: + ra = ReceptorAddress.objects.get(instance=node, canonical=True) + assert ra.port == initial_port + assert ra.peers_from_control_nodes == initial_peers_from + + @pytest.mark.parametrize( + 'payload_port, payload_peers_from', + [ + (27199, True), + (27199, False), + (27199, -1), + ], + ) + def test_creates_canonical_address(self, payload_port, payload_peers_from, admin_user, patch): + node = Instance.objects.create(hostname='abc', node_type='hop') + assert ReceptorAddress.objects.filter(instance=node).count() == 0 + + data = {'enabled': True} # Just to have something to post. + if payload_port != -1: + data['listener_port'] = payload_port + if payload_peers_from != -1: + data['peers_from_control_nodes'] = payload_peers_from + + patch( + url=reverse('api:instance_detail', kwargs={'pk': node.pk}), + data=data, + user=admin_user, + expect=200, + ) + + assert ReceptorAddress.objects.filter(instance=node).count() == 1 + ra = ReceptorAddress.objects.get(instance=node, canonical=True) + assert ra.port == payload_port + assert ra.peers_from_control_nodes == (payload_peers_from if payload_peers_from != -1 else False) + + @pytest.mark.parametrize( + 'payload_port, payload_peers_from, initial_port, initial_peers_from', + [ + (None, False, 27199, True), + (None, -1, 27199, True), + (None, False, 27199, False), + (None, -1, 27199, False), + ], + ) + def test_deletes_canonical_address(self, payload_port, payload_peers_from, initial_port, initial_peers_from, admin_user, patch): + node = Instance.objects.create(hostname='abc', node_type='hop') + ReceptorAddress.objects.create(address=node.hostname, port=initial_port, canonical=True, peers_from_control_nodes=initial_peers_from, instance=node) + + assert ReceptorAddress.objects.filter(instance=node).count() == 1 + + data = {'enabled': True} # Just to have something to post. + if payload_port != -1: + data['listener_port'] = payload_port + if payload_peers_from != -1: + data['peers_from_control_nodes'] = payload_peers_from + + patch( + url=reverse('api:instance_detail', kwargs={'pk': node.pk}), + data=data, + user=admin_user, + expect=200, + ) + + assert ReceptorAddress.objects.filter(instance=node).count() == 0 + + @pytest.mark.parametrize( + 'payload_port, payload_peers_from, initial_port, initial_peers_from', + [ + (27199, True, 27199, False), + (27199, False, 27199, True), + (-1, True, 27199, False), + (-1, False, 27199, True), + ], + ) + def test_updates_canonical_address(self, payload_port, payload_peers_from, initial_port, initial_peers_from, admin_user, patch): + node = Instance.objects.create(hostname='abc', node_type='hop') + ReceptorAddress.objects.create(address=node.hostname, port=initial_port, canonical=True, peers_from_control_nodes=initial_peers_from, instance=node) + + assert ReceptorAddress.objects.filter(instance=node).count() == 1 + + data = {'enabled': True} # Just to have something to post. + if payload_port != -1: + data['listener_port'] = payload_port + if payload_peers_from != -1: + data['peers_from_control_nodes'] = payload_peers_from + + patch( + url=reverse('api:instance_detail', kwargs={'pk': node.pk}), + data=data, + user=admin_user, + expect=200, + ) + + assert ReceptorAddress.objects.filter(instance=node).count() == 1 + ra = ReceptorAddress.objects.get(instance=node, canonical=True) + assert ra.port == initial_port # At the present time, changing ports is not allowed + assert ra.peers_from_control_nodes == payload_peers_from + + @pytest.mark.parametrize( + 'payload_port, payload_peers_from, initial_port, initial_peers_from, error_msg', + [ + (-1, True, None, None, "Cannot enable peers_from_control_nodes"), + (None, True, None, None, "Cannot enable peers_from_control_nodes"), + (None, True, 21799, True, "Cannot enable peers_from_control_nodes"), + (None, True, 21799, False, "Cannot enable peers_from_control_nodes"), + (21800, -1, 21799, True, "Cannot change listener port"), + (21800, True, 21799, True, "Cannot change listener port"), + (21800, False, 21799, True, "Cannot change listener port"), + (21800, -1, 21799, False, "Cannot change listener port"), + (21800, True, 21799, False, "Cannot change listener port"), + (21800, False, 21799, False, "Cannot change listener port"), + ], + ) + def test_canonical_address_validation_error(self, payload_port, payload_peers_from, initial_port, initial_peers_from, error_msg, admin_user, patch): + node = Instance.objects.create(hostname='abc', node_type='hop') + if initial_port is not None: + ReceptorAddress.objects.create(address=node.hostname, port=initial_port, canonical=True, peers_from_control_nodes=initial_peers_from, instance=node) + + assert ReceptorAddress.objects.filter(instance=node).count() == 1 + else: + assert ReceptorAddress.objects.filter(instance=node).count() == 0 + + data = {'enabled': True} # Just to have something to post. + if payload_port != -1: + data['listener_port'] = payload_port + if payload_peers_from != -1: + data['peers_from_control_nodes'] = payload_peers_from + + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': node.pk}), + data=data, + user=admin_user, + expect=400, + ) + + assert error_msg in str(resp.data) + + def test_changing_managed_listener_port(self, admin_user, patch): + """ + if instance is managed, cannot change listener port at all + """ + hop = Instance.objects.create(hostname='abc', node_type="hop", managed=True) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"listener_port": 5678}, + user=admin_user, + expect=400, # cannot set port + ) + assert 'Cannot change listener port for managed nodes.' in str(resp.data) + ReceptorAddress.objects.create(instance=hop, address='hop', port=27199, canonical=True) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"listener_port": None}, + user=admin_user, + expect=400, # cannot unset port + ) + assert 'Cannot change listener port for managed nodes.' in str(resp.data) + + def test_bidirectional_peering(self, admin_user, patch): + """ + cannot peer to node that is already to peered to it + if A -> B, then disallow B -> A + """ + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop1addr = ReceptorAddress.objects.create(instance=hop1, address='hop1', canonical=True) + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + hop2addr = ReceptorAddress.objects.create(instance=hop2, address='hop2', canonical=True) + hop1.peers.add(hop2addr) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop2.pk}), + data={"peers": [hop1addr.id]}, + user=admin_user, + expect=400, + ) + assert 'Instance hop1 is already peered to this instance.' in str(resp.data) + + def test_multiple_peers_same_instance(self, admin_user, patch): + """ + cannot peer to more than one address of the same instance + """ + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop1addr1 = ReceptorAddress.objects.create(instance=hop1, address='hop1', canonical=True) + hop1addr2 = ReceptorAddress.objects.create(instance=hop1, address='hop1alternate') + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop2.pk}), + data={"peers": [hop1addr1.id, hop1addr2.id]}, + user=admin_user, + expect=400, + ) + assert 'Cannot peer to the same instance more than once.' in str(resp.data) + + @pytest.mark.parametrize('node_type', ['control', 'hybrid']) + def test_changing_peers_control_nodes(self, node_type, admin_user, patch): + """ + for control nodes, peers field should not be + modified directly via patch. + """ + control = Instance.objects.create(hostname='abc', node_type=node_type, managed=True) + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop1addr = ReceptorAddress.objects.create(instance=hop1, address='hop1', peers_from_control_nodes=True, canonical=True) + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + hop2addr = ReceptorAddress.objects.create(instance=hop2, address='hop2', canonical=True) + assert [hop1addr] == list(control.peers.all()) # only hop1addr should be peered + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': control.pk}), + data={"peers": [hop2addr.id]}, + user=admin_user, + expect=400, # cannot add peers manually + ) + assert 'Setting peers manually for managed nodes is not allowed.' in str(resp.data) + + patch( + url=reverse('api:instance_detail', kwargs={'pk': control.pk}), + data={"peers": [hop1addr.id]}, + user=admin_user, + expect=200, # patching with current peers list should be okay + ) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': control.pk}), + data={"peers": []}, + user=admin_user, + expect=400, # cannot remove peers directly + ) + assert 'Setting peers manually for managed nodes is not allowed.' in str(resp.data) + + patch( + url=reverse('api:instance_detail', kwargs={'pk': control.pk}), + data={}, + user=admin_user, + expect=200, # patching without data should be fine too + ) + # patch hop2 + patch( + url=reverse('api:instance_detail', kwargs={'pk': hop2.pk}), + data={"peers_from_control_nodes": True}, + user=admin_user, + expect=200, + ) + assert {hop1addr, hop2addr} == set(control.peers.all()) # hop1 and hop2 should now be peered from control node + + def test_changing_hostname(self, admin_user, patch): + """ + cannot change hostname + """ + hop = Instance.objects.create(hostname='hop', node_type='hop') + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"hostname": "hop2"}, + user=admin_user, + expect=400, + ) + + assert 'Cannot change hostname.' in str(resp.data) + + def test_changing_node_state(self, admin_user, patch): + """ + only allow setting to deprovisioning + """ + hop = Instance.objects.create(hostname='hop', node_type='hop', node_state='installed') + patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"node_state": "deprovisioning"}, + user=admin_user, + expect=200, + ) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"node_state": "ready"}, + user=admin_user, + expect=400, + ) + assert "Can only change instances to the 'deprovisioning' state." in str(resp.data) + + def test_changing_managed_node_state(self, admin_user, patch): + """ + cannot change node state of managed node + """ + hop = Instance.objects.create(hostname='hop', node_type='hop', managed=True) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"node_state": "deprovisioning"}, + user=admin_user, + expect=400, + ) + + assert 'Cannot deprovision managed nodes.' in str(resp.data) + + def test_changing_managed_peers_from_control_nodes(self, admin_user, patch): + """ + cannot change peers_from_control_nodes of managed node + """ + hop = Instance.objects.create(hostname='hop', node_type='hop', managed=True) + ReceptorAddress.objects.create(instance=hop, address='hop', peers_from_control_nodes=True, canonical=True) + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"peers_from_control_nodes": False}, + user=admin_user, + expect=400, + ) + + assert 'Cannot change peers_from_control_nodes for managed nodes.' in str(resp.data) + + hop.peers_from_control_nodes = False + hop.save() + + resp = patch( + url=reverse('api:instance_detail', kwargs={'pk': hop.pk}), + data={"peers_from_control_nodes": False}, + user=admin_user, + expect=400, + ) + + assert 'Cannot change peers_from_control_nodes for managed nodes.' in str(resp.data) + + @pytest.mark.parametrize('node_type', ['control', 'hybrid']) + def test_control_node_automatically_peers(self, node_type): + """ + a new control node should automatically + peer to hop + + peer to hop should be removed if hop is deleted + """ + + hop = Instance.objects.create(hostname='hop', node_type='hop') + hopaddr = ReceptorAddress.objects.create(instance=hop, address='hop', peers_from_control_nodes=True, canonical=True) + control = Instance.objects.create(hostname='abc', node_type=node_type) + assert hopaddr in control.peers.all() + hop.delete() + assert not control.peers.exists() + + @pytest.mark.parametrize('node_type', ['control', 'hybrid']) + def test_control_node_retains_other_peers(self, node_type): + """ + if a new node comes online, other peer relationships should + remain intact + """ + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + hop2addr = ReceptorAddress.objects.create(instance=hop2, address='hop2', canonical=True) + hop1.peers.add(hop2addr) + + # a control node is added + Instance.objects.create(hostname='control', node_type=node_type) + + assert hop1.peers.exists() + + def test_reverse_peers(self, admin_user, get): + """ + if hop1 peers to hop2, hop1 should + be in hop2's reverse_peers list + """ + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + hop2addr = ReceptorAddress.objects.create(instance=hop2, address='hop2', canonical=True) + hop1.peers.add(hop2addr) + + resp = get( + url=reverse('api:instance_detail', kwargs={'pk': hop2.pk}), + user=admin_user, + expect=200, + ) + + assert hop1.pk in resp.data['reverse_peers'] + + def test_group_vars(self): + """ + control > hop1 > hop2 < execution + """ + control = Instance.objects.create(hostname='control', node_type='control') + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + ReceptorAddress.objects.create(instance=hop1, address='hop1', peers_from_control_nodes=True, port=6789, canonical=True) + + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + hop2addr = ReceptorAddress.objects.create(instance=hop2, address='hop2', peers_from_control_nodes=False, port=6789, canonical=True) + + execution = Instance.objects.create(hostname='execution', node_type='execution') + ReceptorAddress.objects.create(instance=execution, address='execution', peers_from_control_nodes=False, port=6789, canonical=True) + + execution.peers.add(hop2addr) + hop1.peers.add(hop2addr) + + control_vars = yaml.safe_load(generate_group_vars_all_yml(control)) + hop1_vars = yaml.safe_load(generate_group_vars_all_yml(hop1)) + hop2_vars = yaml.safe_load(generate_group_vars_all_yml(hop2)) + execution_vars = yaml.safe_load(generate_group_vars_all_yml(execution)) + + # control group vars assertions + assert has_peer(control_vars, 'hop1:6789') + assert not has_peer(control_vars, 'hop2:6789') + assert not has_peer(control_vars, 'execution:6789') + assert not control_vars.get('receptor_listener', False) + + # hop1 group vars assertions + assert has_peer(hop1_vars, 'hop2:6789') + assert not has_peer(hop1_vars, 'execution:6789') + assert hop1_vars.get('receptor_listener', False) + + # hop2 group vars assertions + assert not has_peer(hop2_vars, 'hop1:6789') + assert not has_peer(hop2_vars, 'execution:6789') + assert hop2_vars.get('receptor_listener', False) + assert hop2_vars.get('receptor_peers', []) == [] + + # execution group vars assertions + assert has_peer(execution_vars, 'hop2:6789') + assert not has_peer(execution_vars, 'hop1:6789') + assert execution_vars.get('receptor_listener', False) + + def test_write_receptor_config_called(self): + """ + Assert that write_receptor_config is called + when certain instances are created, or if + peers_from_control_nodes changes. + In general, write_receptor_config should only + be called when necessary, as it will reload + receptor backend connections which is not trivial. + """ + with mock.patch('awx.main.models.ha.schedule_write_receptor_config') as write_method: + # new control instance but nothing to peer to (no) + control = Instance.objects.create(hostname='control1', node_type='control') + write_method.assert_not_called() + + # new address with peers_from_control_nodes False (no) + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop1addr = ReceptorAddress.objects.create(instance=hop1, address='hop1', peers_from_control_nodes=False, canonical=True) + hop1.delete() + write_method.assert_not_called() + + # new address with peers_from_control_nodes True (yes) + hop1 = Instance.objects.create(hostname='hop1', node_type='hop') + hop1addr = ReceptorAddress.objects.create(instance=hop1, address='hop1', peers_from_control_nodes=True, canonical=True) + write_method.assert_called() + write_method.reset_mock() + + # new control instance but with something to peer to (yes) + Instance.objects.create(hostname='control2', node_type='control') + write_method.assert_called() + write_method.reset_mock() + + # new address with peers_from_control_nodes False and peered to another hop node (no) + hop2 = Instance.objects.create(hostname='hop2', node_type='hop') + ReceptorAddress.objects.create(instance=hop2, address='hop2', peers_from_control_nodes=False, canonical=True) + hop2.peers.add(hop1addr) + hop2.delete() + write_method.assert_not_called() + + # changing peers_from_control_nodes to False (yes) + hop1addr.peers_from_control_nodes = False + hop1addr.save() + write_method.assert_called() + write_method.reset_mock() + + # deleting address that has peers_from_control_nodes to False (no) + hop1.delete() # cascade deletes to hop1addr + write_method.assert_not_called() + + # deleting control nodes (no) + control.delete() + write_method.assert_not_called() + + def test_write_receptor_config_data(self): + """ + Assert the correct peers are included in data that will + be written to receptor.conf + """ + from awx.main.tasks.receptor import RECEPTOR_CONFIG_STARTER + + with mock.patch('awx.main.tasks.receptor.read_receptor_config', return_value=list(RECEPTOR_CONFIG_STARTER)): + from awx.main.tasks.receptor import generate_config_data + + _, should_update = generate_config_data() + assert not should_update + + # not peered, so config file should not be updated + for i in range(3): + inst = Instance.objects.create(hostname=f"exNo-{i}", node_type='execution') + ReceptorAddress.objects.create(instance=inst, address=f"exNo-{i}", port=6789, peers_from_control_nodes=False, canonical=True) + _, should_update = generate_config_data() + assert not should_update + + # peered, so config file should be updated + expected_peers = [] + for i in range(3): + expected_peers.append(f"hop-{i}:6789") + inst = Instance.objects.create(hostname=f"hop-{i}", node_type='hop') + ReceptorAddress.objects.create(instance=inst, address=f"hop-{i}", port=6789, peers_from_control_nodes=True, canonical=True) + + for i in range(3): + expected_peers.append(f"exYes-{i}:6789") + inst = Instance.objects.create(hostname=f"exYes-{i}", node_type='execution') + ReceptorAddress.objects.create(instance=inst, address=f"exYes-{i}", port=6789, peers_from_control_nodes=True, canonical=True) + + new_config, should_update = generate_config_data() + assert should_update + + peers = [] + for entry in new_config: + for key, value in entry.items(): + if key == "tcp-peer": + peers.append(value['address']) + + assert set(expected_peers) == set(peers) diff --git a/awx/main/tests/functional/api/test_inventory.py b/awx/main/tests/functional/api/test_inventory.py index 80357a22f96c..da19d62e66f7 100644 --- a/awx/main/tests/functional/api/test_inventory.py +++ b/awx/main/tests/functional/api/test_inventory.py @@ -8,6 +8,7 @@ from awx.api.versioning import reverse from awx.main.models import InventorySource, Inventory, ActivityStream +from awx.main.utils.inventory_vars import update_group_variables @pytest.fixture @@ -17,15 +18,6 @@ def scm_inventory(inventory, project): return inventory -@pytest.fixture -def factory_scm_inventory(inventory, project): - def fn(**kwargs): - with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): - return inventory.inventory_sources.create(source_project=project, overwrite_vars=True, source='scm', **kwargs) - - return fn - - @pytest.mark.django_db def test_inventory_source_notification_on_cloud_only(get, post, inventory_source_factory, user, notification_template): u = user('admin', True) @@ -471,6 +463,26 @@ def test_validating_credential_type(self, organization, inventory, admin_user, p assert 'Cloud-based inventory sources (such as ec2)' in r.data['credential'][0] assert 'require credentials for the matching cloud service' in r.data['credential'][0] + def test_credential_dict_value_returns_400(self, inventory, admin_user, put): + """Passing a dict for the credential field should return 400, not 500. + + Reproduces a bug where int() raises TypeError on non-scalar types + (dict, list) which was uncaught, resulting in a 500 Internal Server Error. + """ + inv_src = InventorySource.objects.create(name='test-src', inventory=inventory, source='ec2') + r = put( + url=reverse('api:inventory_source_detail', kwargs={'pk': inv_src.pk}), + data={ + 'name': 'test-src', + 'inventory': inventory.pk, + 'source': 'ec2', + 'credential': {'username': 'admin', 'password': 'secret'}, + }, + user=admin_user, + expect=400, + ) + assert r.status_code == 400 + def test_vault_credential_not_allowed(self, project, inventory, vault_credential, admin_user, post): """Vault credentials cannot be associated via the deprecated field""" # TODO: when feature is added, add tests to use the related credentials @@ -529,6 +541,20 @@ def test_credentials_relationship_mapping(self, project, inventory, organization patch(url=inv_src.get_absolute_url(), data={'credential': aws_cred.pk}, expect=200, user=admin_user) assert list(inv_src.credentials.values_list('id', flat=True)) == [aws_cred.pk] + @pytest.mark.skip(reason="Delay until AAP-53978 completed") + def test_vmware_cred_create_esxi_source(self, inventory, admin_user, organization, post, get): + """Test that a vmware esxi source can be added with a vmware credential""" + from awx.main.models.credential import Credential, CredentialType + + vmware = CredentialType.defaults['vmware']() + vmware.save() + vmware_cred = Credential.objects.create(credential_type=vmware, name="bar", organization=organization) + inv_src = InventorySource.objects.create(inventory=inventory, name='foobar', source='vmware_esxi') + r = post(url=reverse('api:inventory_source_credentials_list', kwargs={'pk': inv_src.pk}), data={'id': vmware_cred.pk}, expect=204, user=admin_user) + g = get(inv_src.get_absolute_url(), admin_user) + assert r.status_code == 204 + assert g.data['credential'] == vmware_cred.pk + @pytest.mark.django_db class TestControlledBySCM: @@ -594,3 +620,346 @@ def test_adding_inv_src_without_proj_access_prohibited(self, post, project, inve rando, expect=403, ) + + +@pytest.mark.django_db +class TestConstructedInventory: + @pytest.fixture + def constructed_inventory(self, organization): + return Inventory.objects.create(name='constructed-test-inventory', kind='constructed', organization=organization) + + def test_get_constructed_inventory(self, constructed_inventory, admin_user, get): + inv_src = constructed_inventory.inventory_sources.first() + inv_src.update_cache_timeout = 53 + inv_src.save(update_fields=['update_cache_timeout']) + r = get(url=reverse('api:constructed_inventory_detail', kwargs={'pk': constructed_inventory.pk}), user=admin_user, expect=200) + assert r.data['update_cache_timeout'] == 53 + + def test_patch_constructed_inventory(self, constructed_inventory, admin_user, patch): + inv_src = constructed_inventory.inventory_sources.first() + assert inv_src.update_cache_timeout == 0 + assert inv_src.limit == '' + r = patch( + url=reverse('api:constructed_inventory_detail', kwargs={'pk': constructed_inventory.pk}), + data=dict(update_cache_timeout=54, limit='foobar'), + user=admin_user, + expect=200, + ) + assert r.data['update_cache_timeout'] == 54 + inv_src = constructed_inventory.inventory_sources.first() + assert inv_src.update_cache_timeout == 54 + assert inv_src.limit == 'foobar' + + def test_patch_constructed_inventory_generated_source_limits_editable_fields(self, constructed_inventory, admin_user, project, patch): + inv_src = constructed_inventory.inventory_sources.first() + r = patch( + url=inv_src.get_absolute_url(), + data={ + 'source': 'scm', + 'source_project': project.pk, + 'source_path': '', + 'source_vars': 'plugin: a.b.c', + }, + expect=400, + user=admin_user, + ) + assert str(r.data['error'][0]) == "Cannot change field 'source' on a constructed inventory source." + + # Make sure it didn't get updated before we got the error + inv_src_after_err = constructed_inventory.inventory_sources.first() + assert inv_src.id == inv_src_after_err.id + assert inv_src.source == inv_src_after_err.source + assert inv_src.source_project == inv_src_after_err.source_project + assert inv_src.source_path == inv_src_after_err.source_path + assert inv_src.source_vars == inv_src_after_err.source_vars + + def test_patch_constructed_inventory_generated_source_allows_source_vars_edit(self, constructed_inventory, admin_user, patch): + inv_src = constructed_inventory.inventory_sources.first() + patch( + url=inv_src.get_absolute_url(), + data={ + 'source_vars': 'plugin: a.b.c', + }, + expect=200, + user=admin_user, + ) + + inv_src_after_patch = constructed_inventory.inventory_sources.first() + + # sanity checks + assert inv_src.id == inv_src_after_patch.id + assert inv_src.source == 'constructed' + assert inv_src_after_patch.source == 'constructed' + assert inv_src.source_vars == '' + + assert inv_src_after_patch.source_vars == 'plugin: a.b.c' + + def test_create_constructed_inventory(self, constructed_inventory, admin_user, post, organization): + r = post( + url=reverse('api:constructed_inventory_list'), + data=dict(name='constructed-inventory-just-created', kind='constructed', organization=organization.id, update_cache_timeout=55, limit='foobar'), + user=admin_user, + expect=201, + ) + pk = r.data['id'] + constructed_inventory = Inventory.objects.get(pk=pk) + inv_src = constructed_inventory.inventory_sources.first() + assert inv_src.update_cache_timeout == 55 + assert inv_src.limit == 'foobar' + + def test_get_absolute_url_for_constructed_inventory(self, constructed_inventory, admin_user, get): + """ + If we are using the normal inventory API endpoint to look at a + constructed inventory, then we should get a normal inventory API route + back. If we are accessing it via the special constructed inventory + endpoint, then we should get that back. + """ + + url_const = reverse('api:constructed_inventory_detail', kwargs={'pk': constructed_inventory.pk}) + url_inv = reverse('api:inventory_detail', kwargs={'pk': constructed_inventory.pk}) + + const_r = get(url=url_const, user=admin_user, expect=200) + inv_r = get(url=url_inv, user=admin_user, expect=200) + assert const_r.data['url'] == url_const + assert inv_r.data['url'] == url_inv + assert inv_r.data['url'] != const_r.data['url'] + assert inv_r.data['related']['constructed_url'] == url_const + assert const_r.data['related']['constructed_url'] == url_const + + +@pytest.mark.django_db +class TestInventoryAllVariables: + + @staticmethod + def simulate_update_from_source(inv_src, variables_dict, overwrite_vars=True): + """ + Update `inventory` with variables `variables_dict` from source + `inv_src`. + """ + # Perform an update from source the same way it is done in + # `inventory_import.Command._update_inventory`. + new_vars = update_group_variables( + group_id=None, # `None` denotes the 'all' group (which doesn't have a pk). + newvars=variables_dict, + dbvars=inv_src.inventory.variables_dict, + invsrc_id=inv_src.id, + inventory_id=inv_src.inventory.id, + overwrite_vars=overwrite_vars, + ) + inv_src.inventory.variables = json.dumps(new_vars) + inv_src.inventory.save(update_fields=["variables"]) + return new_vars + + def update_and_verify(self, inv_src, new_vars, expect=None, overwrite_vars=True, teststep=None): + """ + Helper: Update from source and verify the new inventory variables. + + :param inv_src: An inventory source object with its inventory property + set to the inventory fixture of the called. + :param dict new_vars: The variables of the inventory source `inv_src`. + :param dict expect: (optional) The expected variables state of the + inventory after the update. If not set or None, expect `new_vars`. + :param bool overwrite_vars: The status of the inventory source option + 'overwrite variables'. Default is `True`. + :raise AssertionError: If the inventory does not contain the expected + variables after the update. + """ + self.simulate_update_from_source(inv_src, new_vars, overwrite_vars=overwrite_vars) + if teststep is not None: + assert inv_src.inventory.variables_dict == (expect if expect is not None else new_vars), f"Test step {teststep}" + else: + assert inv_src.inventory.variables_dict == (expect if expect is not None else new_vars) + + def test_set_variables_through_inventory_details_update(self, inventory, patch, admin_user): + """ + Set an inventory variable by changing the inventory details, simulating + a user edit. + """ + # a: x + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'a: x'}, user=admin_user, expect=200) + inventory.refresh_from_db() + assert inventory.variables_dict == {"a": "x"} + + def test_variables_set_by_user_persist_update_from_src(self, inventory, inventory_source, patch, admin_user): + """ + Verify the special behavior that a variable which originates from a user + edit (instead of a source update), is not removed from the inventory + when a source update with overwrite_vars=True does not contain that + variable. This behavior is considered special because a variable which + originates from a source would actually be deleted. + + In addition, verify that an existing variable which was set by a user + edit can be overwritten by a source update. + """ + # Set two variables via user edit. + patch( + url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), + data={'variables': '{"a": "a_from_user", "b": "b_from_user"}'}, + user=admin_user, + expect=200, + ) + inventory.refresh_from_db() + assert inventory.variables_dict == {'a': 'a_from_user', 'b': 'b_from_user'} + # Update from a source which contains only one of the two variables from + # the previous update. + self.simulate_update_from_source(inventory_source, {'a': 'a_from_source'}) + # Verify inventory variables. + assert inventory.variables_dict == {'a': 'a_from_source', 'b': 'b_from_user'} + + def test_variables_set_through_src_get_removed_on_update_from_same_src(self, inventory, inventory_source, patch, admin_user): + """ + Verify that a variable which originates from a source update, is removed + from the inventory when a source update with overwrite_vars=True does + not contain that variable. + + In addition, verify that an existing variable which was set by a user + edit can be overwritten by a source update. + """ + # Set two variables via update from source. + self.simulate_update_from_source(inventory_source, {'a': 'a_from_source', 'b': 'b_from_source'}) + # Verify inventory variables. + assert inventory.variables_dict == {'a': 'a_from_source', 'b': 'b_from_source'} + # Update from the same source which now contains only one of the two + # variables from the previous update. + self.simulate_update_from_source(inventory_source, {'b': 'b_from_source'}) + # Verify the variable has been deleted from the inventory. + assert inventory.variables_dict == {'b': 'b_from_source'} + + def test_overwrite_variables_through_inventory_details_update(self, inventory, patch, admin_user): + """ + Set and update the inventory variables multiple times by changing the + inventory details via api, simulating user edits. + + Any variables update by means of an inventory details update shall + overwright all existing inventory variables. + """ + # a: x + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'a: x'}, user=admin_user, expect=200) + inventory.refresh_from_db() + assert inventory.variables_dict == {"a": "x"} + # a: x2 + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'a: x2'}, user=admin_user, expect=200) + inventory.refresh_from_db() + assert inventory.variables_dict == {"a": "x2"} + # b: y + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'b: y'}, user=admin_user, expect=200) + inventory.refresh_from_db() + assert inventory.variables_dict == {"b": "y"} + + def test_inventory_group_variables_internal_data(self, inventory, patch, admin_user): + """ + Basic verification of how variable updates are stored internally. + + .. Warning:: + + This test verifies a specific implementation of the inventory + variables update business logic. It may deliver false negatives if + the implementation changes. + """ + # x: a + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'a: x'}, user=admin_user, expect=200) + igv = inventory.inventory_group_variables.first() + assert igv.variables == {'a': [[-1, 'x']]} + # b: y + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'b: y'}, user=admin_user, expect=200) + igv = inventory.inventory_group_variables.first() + assert igv.variables == {'b': [[-1, 'y']]} + + def test_update_then_user_change(self, inventory, patch, admin_user, inventory_source): + """ + 1. Update inventory vars by means of an inventory source update. + 2. Update inventory vars by editing the inventory details (aka a 'user + update'), thereby changing variables values and deleting variables + from the inventory. + + .. Warning:: + + This test partly relies on a specific implementation of the + inventory variables update business logic. It may deliver false + negatives if the implementation changes. + """ + assert inventory_source.inventory_id == inventory.pk # sanity + # ---- Test step 1: Set variables by updating from an inventory source. + self.simulate_update_from_source(inventory_source, {'foo': 'foo_from_source', 'bar': 'bar_from_source'}) + # Verify inventory variables. + assert inventory.variables_dict == {'foo': 'foo_from_source', 'bar': 'bar_from_source'} + # Verify internal storage of variables data. Note that this is + # implementation specific + assert inventory.inventory_group_variables.count() == 1 + igv = inventory.inventory_group_variables.first() + assert igv.variables == {'foo': [[inventory_source.id, 'foo_from_source']], 'bar': [[inventory_source.id, 'bar_from_source']]} + # ---- Test step 2: Change the variables by editing the inventory details. + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'foo: foo_from_user'}, user=admin_user, expect=200) + inventory.refresh_from_db() + # Verify that variable `foo` contains the new value, and that variable + # `bar` has been deleted from the inventory. + assert inventory.variables_dict == {"foo": "foo_from_user"} + # Verify internal storage of variables data. Note that this is + # implementation specific + inventory.inventory_group_variables.count() == 1 + igv = inventory.inventory_group_variables.first() + assert igv.variables == {'foo': [[-1, 'foo_from_user']]} + + def test_monotonic_deletions(self, inventory, patch, admin_user): + """ + Verify the variables history logic for monotonic deletions. + + Monotonic in this context means that the variables are deleted in the + reverse order of their creation. + + 1. Set inventory variable x: 0, expect INV={x: 0} + + (The following steps use overwrite_variables=False) + + 2. Update from source A={x: 1}, expect INV={x: 1} + 3. Update from source B={x: 2}, expect INV={x: 2} + 4. Update from source B={}, expect INV={x: 1} + 5. Update from source A={}, expect INV={x: 0} + """ + inv_src_a = InventorySource.objects.create(name="inv-src-A", inventory=inventory, source="ec2") + inv_src_b = InventorySource.objects.create(name="inv-src-B", inventory=inventory, source="ec2") + # Test step 1: + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'x: 0'}, user=admin_user, expect=200) + inventory.refresh_from_db() + assert inventory.variables_dict == {"x": 0} + # Test step 2: Source A overwrites value of var x + self.update_and_verify(inv_src_a, {"x": 1}, teststep=2) + # Test step 3: Source A overwrites value of var x + self.update_and_verify(inv_src_b, {"x": 2}, teststep=3) + # Test step 4: Value of var x from source A reappears + self.update_and_verify(inv_src_b, {}, expect={"x": 1}, teststep=4) + # Test step 5: Value of var x from initial user edit reappears + self.update_and_verify(inv_src_a, {}, expect={"x": 0}, teststep=5) + + def test_interleaved_deletions(self, inventory, patch, admin_user, inventory_source): + """ + Verify the variables history logic for interleaved deletions. + + Interleaved in this context means that the variables are deleted in a + different order than the sequence of their creation. + + 1. Set inventory variable x: 0, expect INV={x: 0} + 2. Update from source A={x: 1}, expect INV={x: 1} + 3. Update from source B={x: 2}, expect INV={x: 2} + 4. Update from source C={x: 3}, expect INV={x: 3} + 5. Update from source B={}, expect INV={x: 3} + 6. Update from source C={}, expect INV={x: 1} + """ + inv_src_a = InventorySource.objects.create(name="inv-src-A", inventory=inventory, source="ec2") + inv_src_b = InventorySource.objects.create(name="inv-src-B", inventory=inventory, source="ec2") + inv_src_c = InventorySource.objects.create(name="inv-src-C", inventory=inventory, source="ec2") + # Test step 1. Set inventory variable x: 0 + patch(url=reverse('api:inventory_detail', kwargs={'pk': inventory.pk}), data={'variables': 'x: 0'}, user=admin_user, expect=200) + inventory.refresh_from_db() + assert inventory.variables_dict == {"x": 0} + # Test step 2: Source A overwrites value of var x + self.update_and_verify(inv_src_a, {"x": 1}, teststep=2) + # Test step 3: Source B overwrites value of var x + self.update_and_verify(inv_src_b, {"x": 2}, teststep=3) + # Test step 4: Source C overwrites value of var x + self.update_and_verify(inv_src_c, {"x": 3}, teststep=4) + # Test step 5: Value of var x from source C remains unchanged + self.update_and_verify(inv_src_b, {}, expect={"x": 3}, teststep=5) + # Test step 6: Value of var x from source A reappears, because the + # latest update from source B did not contain var x. + self.update_and_verify(inv_src_c, {}, expect={"x": 1}, teststep=6) diff --git a/awx/main/tests/functional/api/test_job.py b/awx/main/tests/functional/api/test_job.py index 53e31e5981a6..8398c9b8c26f 100644 --- a/awx/main/tests/functional/api/test_job.py +++ b/awx/main/tests/functional/api/test_job.py @@ -51,6 +51,16 @@ def test_job_relaunch_permission_denied_response(post, get, inventory, project, r = post(reverse('api:job_relaunch', kwargs={'pk': job.pk}), {}, jt_user, expect=201) +@pytest.mark.django_db +def test_label_sublist(get, admin_user, organization): + job = Job.objects.create() + label = Label.objects.create(organization=organization, name='Steve') + job.labels.add(label) + r = get(url=reverse('api:job_label_list', kwargs={'pk': job.pk}), user=admin_user, expect=200) + assert r.data['count'] == 1 + assert r.data['results'].pop()['id'] == label.id + + @pytest.mark.django_db def test_job_relaunch_prompts_not_accepted_response(post, get, inventory, project, credential, net_credential, machine_credential): jt = JobTemplate.objects.create(name='testjt', inventory=inventory, project=project) @@ -200,6 +210,39 @@ def test_disallowed_http_update_methods(put, patch, post, inventory, project, ad patch(url=reverse('api:job_detail', kwargs={'pk': job.pk}), data={}, user=admin_user, expect=405) +@pytest.mark.django_db +@pytest.mark.parametrize( + "job_type", + [ + 'run', + 'check', + ], +) +def test_job_relaunch_with_job_type(post, inventory, project, machine_credential, admin_user, job_type): + # Create a job template + jt = JobTemplate.objects.create(name='testjt', inventory=inventory, project=project) + + # Set initial job type + init_job_type = 'check' if job_type == 'run' else 'run' + + # Create a job instance + job = jt.create_unified_job(_eager_fields={'job_type': init_job_type}) + + # Perform the POST request + url = reverse('api:job_relaunch', kwargs={'pk': job.pk}) + r = post(url=url, data={'job_type': job_type}, user=admin_user, expect=201) + + # Assert that the response status code is 201 (Created) + assert r.status_code == 201 + + # Retrieve the newly created job from the response + new_job_id = r.data.get('id') + new_job = Job.objects.get(id=new_job_id) + + # Assert that the new job has the correct job type + assert new_job.job_type == job_type + + class TestControllerNode: @pytest.fixture def project_update(self, project): @@ -214,7 +257,7 @@ def adhoc(self, inventory): return AdHocCommand.objects.create(inventory=inventory) @pytest.mark.django_db - def test_field_controller_node_exists(self, sqlite_copy_expert, admin_user, job, project_update, inventory_update, adhoc, get, system_job_factory): + def test_field_controller_node_exists(self, sqlite_copy, admin_user, job, project_update, inventory_update, adhoc, get, system_job_factory): system_job = system_job_factory() r = get(reverse('api:unified_job_list') + '?id={}'.format(job.id), admin_user, expect=200) diff --git a/awx/main/tests/functional/api/test_job_runtime_params.py b/awx/main/tests/functional/api/test_job_runtime_params.py index f477a66ed945..95b55e15b601 100644 --- a/awx/main/tests/functional/api/test_job_runtime_params.py +++ b/awx/main/tests/functional/api/test_job_runtime_params.py @@ -38,11 +38,6 @@ def runtime_data(organization, credentialtype_ssh): ) -@pytest.fixture -def job_with_links(machine_credential, inventory): - return Job.objects.create(name='existing-job', credential=machine_credential, inventory=inventory) - - @pytest.fixture def job_template_prompts(project, inventory, machine_credential): def rf(on_off): @@ -131,11 +126,11 @@ def test_job_ignore_unprompted_vars(runtime_data, job_template_prompts, post, ad mock_job = mocker.MagicMock(spec=Job, id=968, **runtime_data) - with mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation'): - response = post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), runtime_data, admin_user, expect=201) - assert JobTemplate.create_unified_job.called - assert JobTemplate.create_unified_job.call_args == () + mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation') + response = post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), runtime_data, admin_user, expect=201) + assert JobTemplate.create_unified_job.called + assert JobTemplate.create_unified_job.call_args == () # Check that job is serialized correctly job_id = response.data['job'] @@ -167,12 +162,12 @@ def test_job_accept_prompted_vars(runtime_data, job_template_prompts, post, admi mock_job = mocker.MagicMock(spec=Job, id=968, **runtime_data) - with mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation'): - response = post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), runtime_data, admin_user, expect=201) - assert JobTemplate.create_unified_job.called - called_with = data_to_internal(runtime_data) - JobTemplate.create_unified_job.assert_called_with(**called_with) + mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation') + response = post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), runtime_data, admin_user, expect=201) + assert JobTemplate.create_unified_job.called + called_with = data_to_internal(runtime_data) + JobTemplate.create_unified_job.assert_called_with(**called_with) job_id = response.data['job'] assert job_id == 968 @@ -187,11 +182,11 @@ def test_job_accept_empty_tags(job_template_prompts, post, admin_user, mocker): mock_job = mocker.MagicMock(spec=Job, id=968) - with mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation'): - post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), {'job_tags': '', 'skip_tags': ''}, admin_user, expect=201) - assert JobTemplate.create_unified_job.called - assert JobTemplate.create_unified_job.call_args == ({'job_tags': '', 'skip_tags': ''},) + mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation') + post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), {'job_tags': '', 'skip_tags': ''}, admin_user, expect=201) + assert JobTemplate.create_unified_job.called + assert JobTemplate.create_unified_job.call_args == ({'job_tags': '', 'skip_tags': ''},) mock_job.signal_start.assert_called_once() @@ -203,14 +198,14 @@ def test_slice_timeout_forks_need_int(job_template_prompts, post, admin_user, mo mock_job = mocker.MagicMock(spec=Job, id=968) - with mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation'): - response = post( - reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), {'timeout': '', 'job_slice_count': '', 'forks': ''}, admin_user, expect=400 - ) - assert 'forks' in response.data and response.data['forks'][0] == 'A valid integer is required.' - assert 'job_slice_count' in response.data and response.data['job_slice_count'][0] == 'A valid integer is required.' - assert 'timeout' in response.data and response.data['timeout'][0] == 'A valid integer is required.' + mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation') + response = post( + reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), {'timeout': '', 'job_slice_count': '', 'forks': ''}, admin_user, expect=400 + ) + assert 'forks' in response.data and response.data['forks'][0] == 'A valid integer is required.' + assert 'job_slice_count' in response.data and response.data['job_slice_count'][0] == 'A valid integer is required.' + assert 'timeout' in response.data and response.data['timeout'][0] == 'A valid integer is required.' @pytest.mark.django_db @@ -244,12 +239,12 @@ def test_job_accept_prompted_vars_null(runtime_data, job_template_prompts_null, mock_job = mocker.MagicMock(spec=Job, id=968, **runtime_data) - with mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation'): - response = post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), runtime_data, rando, expect=201) - assert JobTemplate.create_unified_job.called - expected_call = data_to_internal(runtime_data) - assert JobTemplate.create_unified_job.call_args == (expected_call,) + mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation') + response = post(reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), runtime_data, rando, expect=201) + assert JobTemplate.create_unified_job.called + expected_call = data_to_internal(runtime_data) + assert JobTemplate.create_unified_job.call_args == (expected_call,) job_id = response.data['job'] assert job_id == 968 @@ -641,18 +636,18 @@ def test_job_launch_unprompted_vars_with_survey(mocker, survey_spec_factory, job job_template.survey_spec = survey_spec_factory('survey_var') job_template.save() - with mocker.patch('awx.main.access.BaseAccess.check_license'): - mock_job = mocker.MagicMock(spec=Job, id=968, extra_vars={"job_launch_var": 3, "survey_var": 4}) - with mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation', return_value={}): - response = post( - reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), - dict(extra_vars={"job_launch_var": 3, "survey_var": 4}), - admin_user, - expect=201, - ) - assert JobTemplate.create_unified_job.called - assert JobTemplate.create_unified_job.call_args == ({'extra_vars': {'survey_var': 4}},) + mocker.patch('awx.main.access.BaseAccess.check_license') + mock_job = mocker.MagicMock(spec=Job, id=968, extra_vars={"job_launch_var": 3, "survey_var": 4}) + mocker.patch.object(JobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation', return_value={}) + response = post( + reverse('api:job_template_launch', kwargs={'pk': job_template.pk}), + dict(extra_vars={"job_launch_var": 3, "survey_var": 4}), + admin_user, + expect=201, + ) + assert JobTemplate.create_unified_job.called + assert JobTemplate.create_unified_job.call_args == ({'extra_vars': {'survey_var': 4}},) job_id = response.data['job'] assert job_id == 968 @@ -670,22 +665,22 @@ def test_callback_accept_prompted_extra_var(mocker, survey_spec_factory, job_tem job_template.survey_spec = survey_spec_factory('survey_var') job_template.save() - with mocker.patch('awx.main.access.BaseAccess.check_license'): - mock_job = mocker.MagicMock(spec=Job, id=968, extra_vars={"job_launch_var": 3, "survey_var": 4}) - with mocker.patch.object(UnifiedJobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation', return_value={}): - with mocker.patch('awx.api.views.JobTemplateCallback.find_matching_hosts', return_value=[host]): - post( - reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), - dict(extra_vars={"job_launch_var": 3, "survey_var": 4}, host_config_key="foo"), - admin_user, - expect=201, - format='json', - ) - assert UnifiedJobTemplate.create_unified_job.called - call_args = UnifiedJobTemplate.create_unified_job.call_args[1] - call_args.pop('_eager_fields', None) # internal purposes - assert call_args == {'extra_vars': {'survey_var': 4, 'job_launch_var': 3}, 'limit': 'single-host'} + mocker.patch('awx.main.access.BaseAccess.check_license') + mock_job = mocker.MagicMock(spec=Job, id=968, extra_vars={"job_launch_var": 3, "survey_var": 4}) + mocker.patch.object(UnifiedJobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation', return_value={}) + mocker.patch('awx.api.views.JobTemplateCallback.find_matching_hosts', return_value=[host]) + post( + reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), + dict(extra_vars={"job_launch_var": 3, "survey_var": 4}, host_config_key="foo"), + admin_user, + expect=201, + format='json', + ) + assert UnifiedJobTemplate.create_unified_job.called + call_args = UnifiedJobTemplate.create_unified_job.call_args[1] + call_args.pop('_eager_fields', None) # internal purposes + assert call_args == {'extra_vars': {'survey_var': 4, 'job_launch_var': 3}, 'limit': 'single-host'} mock_job.signal_start.assert_called_once() @@ -697,22 +692,22 @@ def test_callback_ignore_unprompted_extra_var(mocker, survey_spec_factory, job_t job_template.host_config_key = "foo" job_template.save() - with mocker.patch('awx.main.access.BaseAccess.check_license'): - mock_job = mocker.MagicMock(spec=Job, id=968, extra_vars={"job_launch_var": 3, "survey_var": 4}) - with mocker.patch.object(UnifiedJobTemplate, 'create_unified_job', return_value=mock_job): - with mocker.patch('awx.api.serializers.JobSerializer.to_representation', return_value={}): - with mocker.patch('awx.api.views.JobTemplateCallback.find_matching_hosts', return_value=[host]): - post( - reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), - dict(extra_vars={"job_launch_var": 3, "survey_var": 4}, host_config_key="foo"), - admin_user, - expect=201, - format='json', - ) - assert UnifiedJobTemplate.create_unified_job.called - call_args = UnifiedJobTemplate.create_unified_job.call_args[1] - call_args.pop('_eager_fields', None) # internal purposes - assert call_args == {'limit': 'single-host'} + mocker.patch('awx.main.access.BaseAccess.check_license') + mock_job = mocker.MagicMock(spec=Job, id=968, extra_vars={"job_launch_var": 3, "survey_var": 4}) + mocker.patch.object(UnifiedJobTemplate, 'create_unified_job', return_value=mock_job) + mocker.patch('awx.api.serializers.JobSerializer.to_representation', return_value={}) + mocker.patch('awx.api.views.JobTemplateCallback.find_matching_hosts', return_value=[host]) + post( + reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), + dict(extra_vars={"job_launch_var": 3, "survey_var": 4}, host_config_key="foo"), + admin_user, + expect=201, + format='json', + ) + assert UnifiedJobTemplate.create_unified_job.called + call_args = UnifiedJobTemplate.create_unified_job.call_args[1] + call_args.pop('_eager_fields', None) # internal purposes + assert call_args == {'limit': 'single-host'} mock_job.signal_start.assert_called_once() @@ -725,9 +720,9 @@ def test_callback_find_matching_hosts(mocker, get, job_template_prompts, admin_u job_template.save() host_with_alias = Host(name='localhost', inventory=job_template.inventory) host_with_alias.save() - with mocker.patch('awx.main.access.BaseAccess.check_license'): - r = get(reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), user=admin_user, expect=200) - assert tuple(r.data['matching_hosts']) == ('localhost',) + mocker.patch('awx.main.access.BaseAccess.check_license') + r = get(reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), user=admin_user, expect=200) + assert tuple(r.data['matching_hosts']) == ('localhost',) @pytest.mark.django_db @@ -738,6 +733,6 @@ def test_callback_extra_var_takes_priority_over_host_name(mocker, get, job_templ job_template.save() host_with_alias = Host(name='localhost', variables={'ansible_host': 'foobar'}, inventory=job_template.inventory) host_with_alias.save() - with mocker.patch('awx.main.access.BaseAccess.check_license'): - r = get(reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), user=admin_user, expect=200) - assert not r.data['matching_hosts'] + mocker.patch('awx.main.access.BaseAccess.check_license') + r = get(reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), user=admin_user, expect=200) + assert not r.data['matching_hosts'] diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index dc6ce0c7a008..4cdf43d37b9f 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -1,17 +1,23 @@ import pytest +from unittest import mock # AWX from awx.api.serializers import JobTemplateSerializer from awx.api.versioning import reverse -from awx.main.models import Job, JobTemplate, CredentialType, WorkflowJobTemplate, Organization, Project +from awx.main.models import Job, JobTemplate, CredentialType, WorkflowJobTemplate, Organization, Project, Inventory from awx.main.migrations import _save_password_keys as save_password_keys # Django from django.apps import apps +from django.test.utils import override_settings # DRF from rest_framework.exceptions import ValidationError +# DAB +from ansible_base.jwt_consumer.common.util import generate_x_trusted_proxy_header +from ansible_base.lib.testing.fixtures import rsa_keypair_factory, rsa_keypair # noqa: F401; pylint: disable=unused-import + @pytest.mark.django_db @pytest.mark.parametrize( @@ -353,3 +359,129 @@ def test_job_template_branch_prompt_error(project, inventory, post, admin_user): expect=400, ) assert 'Project does not allow overriding branch' in str(r.data['ask_scm_branch_on_launch']) + + +@pytest.mark.django_db +def test_job_template_missing_inventory(project, inventory, admin_user, post): + jt = JobTemplate.objects.create( + name='test-jt', inventory=inventory, ask_inventory_on_launch=True, project=project, playbook='helloworld.yml', host_config_key='abcd' + ) + Inventory.objects.get(pk=inventory.pk).delete() + r = post( + url=reverse('api:job_template_callback', kwargs={'pk': jt.pk}), + data={'host_config_key': 'abcd'}, + user=admin_user, + expect=400, + ) + assert r.status_code == 400 + assert "Cannot start automatically, an inventory is required." in str(r.data) + + +@pytest.mark.django_db +class TestJobTemplateCallbackProxyIntegration: + """ + Test the interaction of provision job template callback feature and: + settings.PROXY_IP_ALLOWED_LIST + x-trusted-proxy http header + """ + + @pytest.fixture + def job_template(self, inventory, project): + jt = JobTemplate.objects.create(name='test-jt', inventory=inventory, project=project, playbook='helloworld.yml', host_config_key='abcd') + return jt + + @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=['my.proxy.example.org']) + def test_host_not_found(self, job_template, admin_user, post, rsa_keypair): # noqa: F811 + job_template.inventory.hosts.create(name='foobar') + + headers = { + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'baz', + 'REMOTE_HOST': 'baz', + 'REMOTE_ADDR': 'baz', + } + r = post( + url=reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), data={'host_config_key': 'abcd'}, user=admin_user, expect=400, **headers + ) + assert r.data['msg'] == 'No matching host could be found!' + + @pytest.mark.parametrize( + 'headers, expected', + ( + pytest.param( + { + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'foobar', + 'REMOTE_HOST': 'my.proxy.example.org', + }, + 201, + ), + pytest.param( + { + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'foobar', + 'REMOTE_HOST': 'not-my-proxy.org', + }, + 400, + ), + ), + ) + @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=['my.proxy.example.org']) + def test_proxy_ip_allowed_list(self, job_template, admin_user, post, headers, expected): # noqa: F811 + job_template.inventory.hosts.create(name='my.proxy.example.org') + + post( + url=reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), + data={'host_config_key': 'abcd'}, + user=admin_user, + expect=expected, + **headers + ) + + @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=[]) + def test_no_proxy_trust_all_headers(self, job_template, admin_user, post): + job_template.inventory.hosts.create(name='foobar') + + headers = { + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'foobar', + 'REMOTE_ADDR': 'bar', + 'REMOTE_HOST': 'baz', + } + post(url=reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), data={'host_config_key': 'abcd'}, user=admin_user, expect=201, **headers) + + @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=['my.proxy.example.org']) + def test_trusted_proxy(self, job_template, admin_user, post, rsa_keypair): # noqa: F811 + job_template.inventory.hosts.create(name='foobar') + + headers = { + 'HTTP_X_TRUSTED_PROXY': generate_x_trusted_proxy_header(rsa_keypair.private), + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'foobar, my.proxy.example.org', + } + + with mock.patch('ansible_base.jwt_consumer.common.cache.JWTCache.get_key_from_cache', lambda self: None): + with override_settings(ANSIBLE_BASE_JWT_KEY=rsa_keypair.public): + post( + url=reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), + data={'host_config_key': 'abcd'}, + user=admin_user, + expect=201, + **headers + ) + + @override_settings(REMOTE_HOST_HEADERS=['HTTP_X_FROM_THE_LOAD_BALANCER', 'REMOTE_ADDR', 'REMOTE_HOST'], PROXY_IP_ALLOWED_LIST=['my.proxy.example.org']) + def test_trusted_proxy_host_not_found(self, job_template, admin_user, post, rsa_keypair): # noqa: F811 + job_template.inventory.hosts.create(name='foobar') + + headers = { + 'HTTP_X_TRUSTED_PROXY': generate_x_trusted_proxy_header(rsa_keypair.private), + 'HTTP_X_FROM_THE_LOAD_BALANCER': 'baz, my.proxy.example.org', + 'REMOTE_ADDR': 'bar', + 'REMOTE_HOST': 'baz', + } + + with mock.patch('ansible_base.jwt_consumer.common.cache.JWTCache.get_key_from_cache', lambda self: None): + with override_settings(ANSIBLE_BASE_JWT_KEY=rsa_keypair.public): + post( + url=reverse('api:job_template_callback', kwargs={'pk': job_template.pk}), + data={'host_config_key': 'abcd'}, + user=admin_user, + expect=400, + **headers + ) diff --git a/awx/main/tests/functional/api/test_license_cache_clearing.py b/awx/main/tests/functional/api/test_license_cache_clearing.py new file mode 100644 index 000000000000..08b8e3513822 --- /dev/null +++ b/awx/main/tests/functional/api/test_license_cache_clearing.py @@ -0,0 +1,191 @@ +import pytest +from unittest.mock import patch, MagicMock + +from awx.api.versioning import reverse + + +# Generated by Cursor (claude-4-sonnet) +@pytest.mark.django_db +class TestLicenseCacheClearing: + """Test cache clearing for LICENSE setting changes""" + + def test_license_from_manifest_clears_cache(self, admin_user, post): + """Test that posting a manifest to /api/v2/config/ clears the LICENSE cache""" + + # Mock the licenser and clear_setting_cache + with patch('awx.api.views.root.get_licenser') as mock_get_licenser, patch('awx.api.views.root.validate_entitlement_manifest') as mock_validate, patch( + 'awx.api.views.root.clear_setting_cache' + ) as mock_clear_cache, patch('django.db.connection.on_commit') as mock_on_commit: + + # Set up mock license data + mock_license_data = {'valid_key': True, 'license_type': 'enterprise', 'instance_count': 100, 'subscription_name': 'Test Enterprise License'} + + # Mock the validation and license processing + mock_validate.return_value = [{'some': 'manifest_data'}] + mock_licenser = MagicMock() + mock_licenser.license_from_manifest.return_value = mock_license_data + mock_get_licenser.return_value = mock_licenser + + # Prepare the request data (base64 encoded manifest) + manifest_data = {'manifest': 'ZmFrZS1tYW5pZmVzdC1kYXRh'} # base64 for "fake-manifest-data" + + # Make the POST request + url = reverse('api:api_v2_config_view') + response = post(url, manifest_data, admin_user, expect=200) + + # Verify the response + assert response.data == mock_license_data + + # Verify license_from_manifest was called + mock_licenser.license_from_manifest.assert_called_once() + + # Verify on_commit was called (may be multiple times due to other settings) + assert mock_on_commit.call_count >= 1 + + # Execute all on_commit callbacks to trigger cache clearing + for call_args in mock_on_commit.call_args_list: + callback = call_args[0][0] + callback() + + # Verify that clear_setting_cache.delay was called with ['LICENSE'] + mock_clear_cache.delay.assert_any_call(['LICENSE']) + + def test_config_delete_clears_cache(self, admin_user, delete): + """Test that DELETE /api/v2/config/ clears the LICENSE cache""" + + with patch('awx.api.views.root.clear_setting_cache') as mock_clear_cache, patch('django.db.connection.on_commit') as mock_on_commit: + + # Make the DELETE request + url = reverse('api:api_v2_config_view') + delete(url, admin_user, expect=204) + + # Verify on_commit was called at least once + assert mock_on_commit.call_count >= 1 + + # Execute all on_commit callbacks to trigger cache clearing + for call_args in mock_on_commit.call_args_list: + callback = call_args[0][0] + callback() + + mock_clear_cache.delay.assert_called_once_with(['LICENSE']) + + def test_attach_view_clears_cache(self, admin_user, post): + """Test that posting to /api/v2/config/attach/ clears the LICENSE cache""" + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser, patch('awx.api.views.root.clear_setting_cache') as mock_clear_cache, patch( + 'django.db.connection.on_commit' + ) as mock_on_commit, patch('awx.api.views.root.settings') as mock_settings: + + # Set up subscription credentials in settings + mock_settings.SUBSCRIPTIONS_CLIENT_ID = 'test-client-id' + mock_settings.SUBSCRIPTIONS_CLIENT_SECRET = 'test-client-secret' + + # Set up mock licenser with validated subscriptions + mock_licenser = MagicMock() + subscription_data = {'subscription_id': 'test-subscription-123', 'valid_key': False, 'license_type': 'enterprise', 'instance_count': 50} + mock_licenser.validate_rh.return_value = [subscription_data] + mock_get_licenser.return_value = mock_licenser + + # Prepare request data + request_data = {'subscription_id': 'test-subscription-123'} + + # Make the POST request + url = reverse('api:api_v2_attach_view') + response = post(url, request_data, admin_user, expect=200) + + # Verify the response includes valid_key=True + assert response.data['valid_key'] is True + assert response.data['subscription_id'] == 'test-subscription-123' + + # Verify settings.LICENSE was set + expected_license = subscription_data.copy() + expected_license['valid_key'] = True + assert mock_settings.LICENSE == expected_license + + # Verify cache clearing was scheduled + mock_on_commit.assert_called_once() + call_args = mock_on_commit.call_args[0][0] # Get the lambda function + + # Execute the lambda to verify it calls clear_setting_cache + call_args() + mock_clear_cache.delay.assert_called_once_with(['LICENSE']) + + def test_attach_view_subscription_not_found_no_cache_clear(self, admin_user, post): + """Test that attach view doesn't clear cache when subscription is not found""" + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser, patch('awx.api.views.root.clear_setting_cache') as mock_clear_cache, patch( + 'django.db.connection.on_commit' + ) as mock_on_commit: + + # Set up mock licenser with different subscription + mock_licenser = MagicMock() + subscription_data = {'subscription_id': 'different-subscription-456', 'valid_key': False, 'license_type': 'enterprise'} # Different ID + mock_licenser.validate_rh.return_value = [subscription_data] + mock_get_licenser.return_value = mock_licenser + + # Request data with non-matching subscription ID + request_data = { + 'subscription_id': 'test-subscription-123', # This won't match + } + + # Make the POST request + url = reverse('api:api_v2_attach_view') + response = post(url, request_data, admin_user, expect=400) + + # Verify error response + assert 'error' in response.data + + # Verify cache clearing was NOT called (no matching subscription) + mock_on_commit.assert_not_called() + mock_clear_cache.delay.assert_not_called() + + def test_manifest_validation_error_no_cache_clear(self, admin_user, post): + """Test that config view doesn't clear cache when manifest validation fails""" + + with patch('awx.api.views.root.validate_entitlement_manifest') as mock_validate, patch( + 'awx.api.views.root.clear_setting_cache' + ) as mock_clear_cache, patch('django.db.connection.on_commit') as mock_on_commit: + + # Mock validation to raise ValueError + mock_validate.side_effect = ValueError("Invalid manifest") + + # Prepare request data + manifest_data = {'manifest': 'aW52YWxpZC1tYW5pZmVzdA=='} # base64 for "invalid-manifest" + + # Make the POST request + url = reverse('api:api_v2_config_view') + response = post(url, manifest_data, admin_user, expect=400) + + # Verify error response + assert response.data['error'] == 'Invalid manifest' + + # Verify cache clearing was NOT called (validation failed) + mock_on_commit.assert_not_called() + mock_clear_cache.delay.assert_not_called() + + def test_license_processing_error_no_cache_clear(self, admin_user, post): + """Test that config view doesn't clear cache when license processing fails""" + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser, patch('awx.api.views.root.validate_entitlement_manifest') as mock_validate, patch( + 'awx.api.views.root.clear_setting_cache' + ) as mock_clear_cache, patch('django.db.connection.on_commit') as mock_on_commit: + + # Mock validation to succeed but license processing to fail + mock_validate.return_value = [{'some': 'manifest_data'}] + mock_licenser = MagicMock() + mock_licenser.license_from_manifest.side_effect = Exception("License processing failed") + mock_get_licenser.return_value = mock_licenser + + # Prepare request data + manifest_data = {'manifest': 'ZmFrZS1tYW5pZmVzdA=='} # base64 for "fake-manifest" + + # Make the POST request + url = reverse('api:api_v2_config_view') + response = post(url, manifest_data, admin_user, expect=400) + + # Verify error response + assert response.data['error'] == 'Invalid License' + + # Verify cache clearing was NOT called (license processing failed) + mock_on_commit.assert_not_called() + mock_clear_cache.delay.assert_not_called() diff --git a/awx/main/tests/functional/api/test_licensing.py b/awx/main/tests/functional/api/test_licensing.py new file mode 100644 index 000000000000..a752f0d3f2be --- /dev/null +++ b/awx/main/tests/functional/api/test_licensing.py @@ -0,0 +1,244 @@ +from unittest.mock import patch, MagicMock + +import pytest +from awx.api.versioning import reverse +from rest_framework import status + + +@pytest.mark.django_db +class TestApiV2SubscriptionView: + """Test cases for the /api/v2/config/subscriptions/ endpoint""" + + def test_basic_auth(self, post, admin): + """Test POST with subscriptions_username and subscriptions_password calls validate_rh with basic_auth=True""" + data = {'subscriptions_username': 'test_user', 'subscriptions_password': 'test_password'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + mock_licenser.validate_rh.assert_called_once_with('test_user', 'test_password', True) + + def test_service_account(self, post, admin): + """Test POST with subscriptions_client_id and subscriptions_client_secret calls validate_rh with basic_auth=False""" + data = {'subscriptions_client_id': 'test_client_id', 'subscriptions_client_secret': 'test_client_secret'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + mock_licenser.validate_rh.assert_called_once_with('test_client_id', 'test_client_secret', False) + + def test_encrypted_password_basic_auth(self, post, admin, settings): + """Test POST with $encrypted$ password uses settings value for basic auth""" + data = {'subscriptions_username': 'test_user', 'subscriptions_password': '$encrypted$'} + + settings.SUBSCRIPTIONS_PASSWORD = 'actual_password_from_settings' + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + mock_licenser.validate_rh.assert_called_once_with('test_user', 'actual_password_from_settings', True) + + def test_encrypted_client_secret_service_account(self, post, admin, settings): + """Test POST with $encrypted$ client_secret uses settings value for service_account""" + data = {'subscriptions_client_id': 'test_client_id', 'subscriptions_client_secret': '$encrypted$'} + + settings.SUBSCRIPTIONS_CLIENT_SECRET = 'actual_secret_from_settings' + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + mock_licenser.validate_rh.assert_called_once_with('test_client_id', 'actual_secret_from_settings', False) + + def test_missing_username_returns_error(self, post, admin): + """Test POST with missing username returns 400 error""" + data = {'subscriptions_password': 'test_password'} + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'Missing subscription credentials' in response.data['error'] + + def test_missing_password_returns_error(self, post, admin, settings): + """Test POST with missing password returns 400 error""" + data = {'subscriptions_username': 'test_user'} + settings.SUBSCRIPTIONS_PASSWORD = None + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'Missing subscription credentials' in response.data['error'] + + def test_missing_client_id_returns_error(self, post, admin): + """Test POST with missing client_id returns 400 error""" + data = {'subscriptions_client_secret': 'test_secret'} + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'Missing subscription credentials' in response.data['error'] + + def test_missing_client_secret_returns_error(self, post, admin, settings): + """Test POST with missing client_secret returns 400 error""" + data = {'subscriptions_client_id': 'test_client_id'} + settings.SUBSCRIPTIONS_CLIENT_SECRET = None + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'Missing subscription credentials' in response.data['error'] + + def test_empty_username_returns_error(self, post, admin): + """Test POST with empty username returns 400 error""" + data = {'subscriptions_username': '', 'subscriptions_password': 'test_password'} + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'Missing subscription credentials' in response.data['error'] + + def test_empty_password_returns_error(self, post, admin, settings): + """Test POST with empty password returns 400 error""" + data = {'subscriptions_username': 'test_user', 'subscriptions_password': ''} + settings.SUBSCRIPTIONS_PASSWORD = None + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert 'Missing subscription credentials' in response.data['error'] + + def test_non_superuser_permission_denied(self, post, rando): + """Test that non-superuser cannot access the endpoint""" + data = {'subscriptions_username': 'test_user', 'subscriptions_password': 'test_password'} + + response = post(reverse('api:api_v2_subscription_view'), data, rando) + + assert response.status_code == status.HTTP_403_FORBIDDEN + + def test_settings_updated_on_successful_basic_auth(self, post, admin, settings): + """Test that settings are updated when basic auth validation succeeds""" + data = {'subscriptions_username': 'new_username', 'subscriptions_password': 'new_password'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + assert settings.SUBSCRIPTIONS_USERNAME == 'new_username' + assert settings.SUBSCRIPTIONS_PASSWORD == 'new_password' + + def test_settings_updated_on_successful_service_account(self, post, admin, settings): + """Test that settings are updated when service account validation succeeds""" + data = {'subscriptions_client_id': 'new_client_id', 'subscriptions_client_secret': 'new_client_secret'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + assert settings.SUBSCRIPTIONS_CLIENT_ID == 'new_client_id' + assert settings.SUBSCRIPTIONS_CLIENT_SECRET == 'new_client_secret' + + def test_validate_rh_exception_handling(self, post, admin): + """Test that exceptions from validate_rh are properly handled""" + data = {'subscriptions_username': 'test_user', 'subscriptions_password': 'test_password'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.side_effect = Exception("Connection error") + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + + def test_mixed_credentials_prioritizes_client_id(self, post, admin): + """Test that when both username and client_id are provided, client_id takes precedence""" + data = { + 'subscriptions_username': 'test_user', + 'subscriptions_password': 'test_password', + 'subscriptions_client_id': 'test_client_id', + 'subscriptions_client_secret': 'test_client_secret', + } + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + # Should use service account (basic_auth=False) since client_id is present + mock_licenser.validate_rh.assert_called_once_with('test_client_id', 'test_client_secret', False) + + def test_basic_auth_clears_service_account_settings(self, post, admin, settings): + """Test that setting basic auth credentials clears service account settings""" + # Pre-populate service account settings + settings.SUBSCRIPTIONS_CLIENT_ID = 'existing_client_id' + settings.SUBSCRIPTIONS_CLIENT_SECRET = 'existing_client_secret' + + data = {'subscriptions_username': 'test_user', 'subscriptions_password': 'test_password'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + # Basic auth settings should be set + assert settings.SUBSCRIPTIONS_USERNAME == 'test_user' + assert settings.SUBSCRIPTIONS_PASSWORD == 'test_password' + # Service account settings should be cleared + assert settings.SUBSCRIPTIONS_CLIENT_ID == "" + assert settings.SUBSCRIPTIONS_CLIENT_SECRET == "" + + def test_service_account_clears_basic_auth_settings(self, post, admin, settings): + """Test that setting service account credentials clears basic auth settings""" + # Pre-populate basic auth settings + settings.SUBSCRIPTIONS_USERNAME = 'existing_username' + settings.SUBSCRIPTIONS_PASSWORD = 'existing_password' + + data = {'subscriptions_client_id': 'test_client_id', 'subscriptions_client_secret': 'test_client_secret'} + + with patch('awx.api.views.root.get_licenser') as mock_get_licenser: + mock_licenser = MagicMock() + mock_licenser.validate_rh.return_value = [] + mock_get_licenser.return_value = mock_licenser + + response = post(reverse('api:api_v2_subscription_view'), data, admin) + + assert response.status_code == status.HTTP_200_OK + # Service account settings should be set + assert settings.SUBSCRIPTIONS_CLIENT_ID == 'test_client_id' + assert settings.SUBSCRIPTIONS_CLIENT_SECRET == 'test_client_secret' + # Basic auth settings should be cleared + assert settings.SUBSCRIPTIONS_USERNAME == "" + assert settings.SUBSCRIPTIONS_PASSWORD == "" diff --git a/awx/main/tests/functional/api/test_notifications.py b/awx/main/tests/functional/api/test_notifications.py index 92f604519109..431065396d5f 100644 --- a/awx/main/tests/functional/api/test_notifications.py +++ b/awx/main/tests/functional/api/test_notifications.py @@ -153,3 +153,13 @@ def test_post_org_approval_notification(get, post, admin, notification_template, response = get(url, admin) assert response.status_code == 200 assert len(response.data['results']) == 1 + + +@pytest.mark.django_db +def test_post_wfj_notification(get, post, admin, workflow_job, notification): + workflow_job.notifications.add(notification) + workflow_job.save() + url = reverse("api:workflow_job_notifications_list", kwargs={'pk': workflow_job.pk}) + response = get(url, admin) + assert response.status_code == 200 + assert len(response.data['results']) == 1 diff --git a/awx/main/tests/functional/api/test_oauth.py b/awx/main/tests/functional/api/test_oauth.py deleted file mode 100644 index 4387f06b9caa..000000000000 --- a/awx/main/tests/functional/api/test_oauth.py +++ /dev/null @@ -1,342 +0,0 @@ -import base64 -import json -import time - -import pytest - -from django.db import connection -from django.test.utils import override_settings -from django.utils.encoding import smart_str, smart_bytes - -from awx.main.utils.encryption import decrypt_value, get_encryption_key -from awx.api.versioning import reverse, drf_reverse -from awx.main.models.oauth import OAuth2Application as Application, OAuth2AccessToken as AccessToken -from awx.main.tests.functional import immediate_on_commit -from awx.sso.models import UserEnterpriseAuth -from oauth2_provider.models import RefreshToken - - -@pytest.mark.django_db -def test_personal_access_token_creation(oauth_application, post, alice): - url = drf_reverse('api:oauth_authorization_root_view') + 'token/' - resp = post( - url, - data='grant_type=password&username=alice&password=alice&scope=read', - content_type='application/x-www-form-urlencoded', - HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))), - ) - resp_json = smart_str(resp._container[0]) - assert 'access_token' in resp_json - assert 'scope' in resp_json - assert 'refresh_token' in resp_json - - -@pytest.mark.django_db -@pytest.mark.parametrize('allow_oauth, status', [(True, 201), (False, 403)]) -def test_token_creation_disabled_for_external_accounts(oauth_application, post, alice, allow_oauth, status): - UserEnterpriseAuth(user=alice, provider='radius').save() - url = drf_reverse('api:oauth_authorization_root_view') + 'token/' - - with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USERS=allow_oauth): - resp = post( - url, - data='grant_type=password&username=alice&password=alice&scope=read', - content_type='application/x-www-form-urlencoded', - HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))), - status=status, - ) - if allow_oauth: - assert AccessToken.objects.count() == 1 - else: - assert 'OAuth2 Tokens cannot be created by users associated with an external authentication provider' in smart_str(resp.content) # noqa - assert AccessToken.objects.count() == 0 - - -@pytest.mark.django_db -def test_existing_token_enabled_for_external_accounts(oauth_application, get, post, admin): - UserEnterpriseAuth(user=admin, provider='radius').save() - url = drf_reverse('api:oauth_authorization_root_view') + 'token/' - with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USERS=True): - resp = post( - url, - data='grant_type=password&username=admin&password=admin&scope=read', - content_type='application/x-www-form-urlencoded', - HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))), - status=201, - ) - token = json.loads(resp.content)['access_token'] - assert AccessToken.objects.count() == 1 - - with immediate_on_commit(): - resp = get(drf_reverse('api:user_me_list', kwargs={'version': 'v2'}), HTTP_AUTHORIZATION='Bearer ' + token, status=200) - assert json.loads(resp.content)['results'][0]['username'] == 'admin' - - with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USER=False): - with immediate_on_commit(): - resp = get(drf_reverse('api:user_me_list', kwargs={'version': 'v2'}), HTTP_AUTHORIZATION='Bearer ' + token, status=200) - assert json.loads(resp.content)['results'][0]['username'] == 'admin' - - -@pytest.mark.django_db -def test_pat_creation_no_default_scope(oauth_application, post, admin): - # tests that the default scope is overriden - url = reverse('api:o_auth2_token_list') - response = post( - url, - { - 'description': 'test token', - 'scope': 'read', - 'application': oauth_application.pk, - }, - admin, - ) - assert response.data['scope'] == 'read' - - -@pytest.mark.django_db -def test_pat_creation_no_scope(oauth_application, post, admin): - url = reverse('api:o_auth2_token_list') - response = post( - url, - { - 'description': 'test token', - 'application': oauth_application.pk, - }, - admin, - ) - assert response.data['scope'] == 'write' - - -@pytest.mark.django_db -def test_oauth2_application_create(admin, organization, post): - response = post( - reverse('api:o_auth2_application_list'), - { - 'name': 'test app', - 'organization': organization.pk, - 'client_type': 'confidential', - 'authorization_grant_type': 'password', - }, - admin, - expect=201, - ) - assert 'modified' in response.data - assert 'updated' not in response.data - created_app = Application.objects.get(client_id=response.data['client_id']) - assert created_app.name == 'test app' - assert created_app.skip_authorization is False - assert created_app.redirect_uris == '' - assert created_app.client_type == 'confidential' - assert created_app.authorization_grant_type == 'password' - assert created_app.organization == organization - - -@pytest.mark.django_db -def test_oauth2_validator(admin, oauth_application, post): - post( - reverse('api:o_auth2_application_list'), - { - 'name': 'Write App Token', - 'application': oauth_application.pk, - 'scope': 'Write', - }, - admin, - expect=400, - ) - - -@pytest.mark.django_db -def test_oauth_application_update(oauth_application, organization, patch, admin, alice): - patch( - reverse('api:o_auth2_application_detail', kwargs={'pk': oauth_application.pk}), - { - 'name': 'Test app with immutable grant type and user', - 'organization': organization.pk, - 'redirect_uris': 'http://localhost/api/', - 'authorization_grant_type': 'password', - 'skip_authorization': True, - }, - admin, - expect=200, - ) - updated_app = Application.objects.get(client_id=oauth_application.client_id) - assert updated_app.name == 'Test app with immutable grant type and user' - assert updated_app.redirect_uris == 'http://localhost/api/' - assert updated_app.skip_authorization is True - assert updated_app.authorization_grant_type == 'password' - assert updated_app.organization == organization - - -@pytest.mark.django_db -def test_oauth_application_encryption(admin, organization, post): - response = post( - reverse('api:o_auth2_application_list'), - { - 'name': 'test app', - 'organization': organization.pk, - 'client_type': 'confidential', - 'authorization_grant_type': 'password', - }, - admin, - expect=201, - ) - pk = response.data.get('id') - secret = response.data.get('client_secret') - with connection.cursor() as cursor: - encrypted = cursor.execute('SELECT client_secret FROM main_oauth2application WHERE id={}'.format(pk)).fetchone()[0] - assert encrypted.startswith('$encrypted$') - assert decrypt_value(get_encryption_key('value', pk=None), encrypted) == secret - - -@pytest.mark.django_db -def test_oauth_token_create(oauth_application, get, post, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - assert 'modified' in response.data and response.data['modified'] is not None - assert 'updated' not in response.data - token = AccessToken.objects.get(token=response.data['token']) - refresh_token = RefreshToken.objects.get(token=response.data['refresh_token']) - assert token.application == oauth_application - assert refresh_token.application == oauth_application - assert token.user == admin - assert refresh_token.user == admin - assert refresh_token.access_token == token - assert token.scope == 'read' - response = get(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), admin, expect=200) - assert response.data['count'] == 1 - response = get(reverse('api:o_auth2_application_detail', kwargs={'pk': oauth_application.pk}), admin, expect=200) - assert response.data['summary_fields']['tokens']['count'] == 1 - assert response.data['summary_fields']['tokens']['results'][0] == {'id': token.pk, 'scope': token.scope, 'token': '************'} - - response = post(reverse('api:o_auth2_token_list'), {'scope': 'read', 'application': oauth_application.pk}, admin, expect=201) - assert response.data['refresh_token'] - response = post( - reverse('api:user_authorized_token_list', kwargs={'pk': admin.pk}), {'scope': 'read', 'application': oauth_application.pk}, admin, expect=201 - ) - assert response.data['refresh_token'] - response = post(reverse('api:application_o_auth2_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - assert response.data['refresh_token'] - - -@pytest.mark.django_db -def test_oauth_token_update(oauth_application, post, patch, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - patch(reverse('api:o_auth2_token_detail', kwargs={'pk': token.pk}), {'scope': 'write'}, admin, expect=200) - token = AccessToken.objects.get(token=token.token) - assert token.scope == 'write' - - -@pytest.mark.django_db -def test_oauth_token_delete(oauth_application, post, delete, get, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - delete(reverse('api:o_auth2_token_detail', kwargs={'pk': token.pk}), admin, expect=204) - assert AccessToken.objects.count() == 0 - assert RefreshToken.objects.count() == 1 - response = get(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), admin, expect=200) - assert response.data['count'] == 0 - response = get(reverse('api:o_auth2_application_detail', kwargs={'pk': oauth_application.pk}), admin, expect=200) - assert response.data['summary_fields']['tokens']['count'] == 0 - - -@pytest.mark.django_db -def test_oauth_application_delete(oauth_application, post, delete, admin): - post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - delete(reverse('api:o_auth2_application_detail', kwargs={'pk': oauth_application.pk}), admin, expect=204) - assert Application.objects.filter(client_id=oauth_application.client_id).count() == 0 - assert RefreshToken.objects.filter(application=oauth_application).count() == 0 - assert AccessToken.objects.filter(application=oauth_application).count() == 0 - - -@pytest.mark.django_db -def test_oauth_list_user_tokens(oauth_application, post, get, admin, alice): - for user in (admin, alice): - url = reverse('api:o_auth2_token_list', kwargs={'pk': user.pk}) - post(url, {'scope': 'read'}, user, expect=201) - response = get(url, admin, expect=200) - assert response.data['count'] == 1 - - -@pytest.mark.django_db -def test_refresh_accesstoken(oauth_application, post, get, delete, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - assert AccessToken.objects.count() == 1 - assert RefreshToken.objects.count() == 1 - token = AccessToken.objects.get(token=response.data['token']) - refresh_token = RefreshToken.objects.get(token=response.data['refresh_token']) - - refresh_url = drf_reverse('api:oauth_authorization_root_view') + 'token/' - response = post( - refresh_url, - data='grant_type=refresh_token&refresh_token=' + refresh_token.token, - content_type='application/x-www-form-urlencoded', - HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))), - ) - assert RefreshToken.objects.filter(token=refresh_token).exists() - original_refresh_token = RefreshToken.objects.get(token=refresh_token) - assert token not in AccessToken.objects.all() - assert AccessToken.objects.count() == 1 - # the same RefreshToken remains but is marked revoked - assert RefreshToken.objects.count() == 2 - new_token = json.loads(response._container[0])['access_token'] - new_refresh_token = json.loads(response._container[0])['refresh_token'] - assert AccessToken.objects.filter(token=new_token).count() == 1 - # checks that RefreshTokens are rotated (new RefreshToken issued) - assert RefreshToken.objects.filter(token=new_refresh_token).count() == 1 - assert original_refresh_token.revoked # is not None - - -@pytest.mark.django_db -def test_refresh_token_expiration_is_respected(oauth_application, post, get, delete, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - assert AccessToken.objects.count() == 1 - assert RefreshToken.objects.count() == 1 - refresh_token = RefreshToken.objects.get(token=response.data['refresh_token']) - refresh_url = drf_reverse('api:oauth_authorization_root_view') + 'token/' - short_lived = {'ACCESS_TOKEN_EXPIRE_SECONDS': 1, 'AUTHORIZATION_CODE_EXPIRE_SECONDS': 1, 'REFRESH_TOKEN_EXPIRE_SECONDS': 1} - time.sleep(1) - with override_settings(OAUTH2_PROVIDER=short_lived): - response = post( - refresh_url, - data='grant_type=refresh_token&refresh_token=' + refresh_token.token, - content_type='application/x-www-form-urlencoded', - HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))), - ) - assert response.status_code == 403 - assert b'The refresh token has expired.' in response.content - assert RefreshToken.objects.filter(token=refresh_token).exists() - assert AccessToken.objects.count() == 1 - assert RefreshToken.objects.count() == 1 - - -@pytest.mark.django_db -def test_revoke_access_then_refreshtoken(oauth_application, post, get, delete, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - refresh_token = RefreshToken.objects.get(token=response.data['refresh_token']) - assert AccessToken.objects.count() == 1 - assert RefreshToken.objects.count() == 1 - - token.revoke() - assert AccessToken.objects.count() == 0 - assert RefreshToken.objects.count() == 1 - assert not refresh_token.revoked - - refresh_token.revoke() - assert AccessToken.objects.count() == 0 - assert RefreshToken.objects.count() == 1 - - -@pytest.mark.django_db -def test_revoke_refreshtoken(oauth_application, post, get, delete, admin): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}), {'scope': 'read'}, admin, expect=201) - refresh_token = RefreshToken.objects.get(token=response.data['refresh_token']) - assert AccessToken.objects.count() == 1 - assert RefreshToken.objects.count() == 1 - - refresh_token.revoke() - assert AccessToken.objects.count() == 0 - # the same RefreshToken is recycled - new_refresh_token = RefreshToken.objects.all().first() - assert refresh_token == new_refresh_token - assert new_refresh_token.revoked diff --git a/awx/main/tests/functional/api/test_oidc_credential_feature_flag.py b/awx/main/tests/functional/api/test_oidc_credential_feature_flag.py new file mode 100644 index 000000000000..e76e61528962 --- /dev/null +++ b/awx/main/tests/functional/api/test_oidc_credential_feature_flag.py @@ -0,0 +1,163 @@ +""" +Tests for OIDC workload identity credential type feature flag. + +The FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED flag is an install-time flag that +controls whether OIDC credential types are loaded into the registry at startup. +When disabled, OIDC credential types are not loaded and do not exist in the database. +""" + +import pytest +from unittest import mock + +from django.test import override_settings + +from awx.main.constants import OIDC_CREDENTIAL_TYPE_NAMESPACES +from awx.main.models.credential import CredentialType, ManagedCredentialType, load_credentials +from awx.api.versioning import reverse + + +@pytest.fixture +def reload_credentials_with_flag(django_db_setup, django_db_blocker): + """ + Fixture that reloads credentials with a specific flag state. + This simulates what happens at application startup. + """ + # Save original registry state + original_registry = ManagedCredentialType.registry.copy() + + def _reload(flag_enabled): + with django_db_blocker.unblock(): + # Clear the entire registry before reloading + ManagedCredentialType.registry.clear() + + # Reload credentials with the specified flag state + with override_settings(FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED=flag_enabled): + with mock.patch('awx.main.models.credential.detect_server_product_name', return_value='NOT_AWX'): + load_credentials() + + # Sync to database + CredentialType.setup_tower_managed_defaults(lock=False) + + # In tests, the session fixture pre-loads all credential types into the DB. + # Remove OIDC types when testing the disabled state so the API test is accurate. + if not flag_enabled: + CredentialType.objects.filter(namespace__in=OIDC_CREDENTIAL_TYPE_NAMESPACES).delete() + + yield _reload + + # Restore original registry state after tests + ManagedCredentialType.registry.clear() + ManagedCredentialType.registry.update(original_registry) + + +@pytest.fixture +def isolated_registry(): + """Save and restore the ManagedCredentialType registry, with full isolation via mocked entry_points.""" + original_registry = ManagedCredentialType.registry.copy() + ManagedCredentialType.registry.clear() + yield + ManagedCredentialType.registry.clear() + ManagedCredentialType.registry.update(original_registry) + + +def _make_mock_entry_point(name): + """Create a mock entry point that mimics a credential plugin.""" + ep = mock.MagicMock() + ep.name = name + ep.value = f'test_plugin:{name}' + plugin = mock.MagicMock(spec=[]) + ep.load.return_value = plugin + return ep + + +def _mock_entry_points_factory(managed_names, supported_names): + """Return a side_effect function for mocking entry_points() with controlled plugins.""" + managed = [_make_mock_entry_point(n) for n in managed_names] + supported = [_make_mock_entry_point(n) for n in supported_names] + + def _entry_points(group): + if group == 'awx_plugins.managed_credentials': + return managed + elif group == 'awx_plugins.managed_credentials.supported': + return supported + return [] + + return _entry_points + + +# --- Unit tests for load_credentials() registry behavior --- + + +def test_oidc_types_in_registry_when_flag_enabled(isolated_registry): + """Test that OIDC credential types are added to the registry when flag is enabled.""" + mock_eps = _mock_entry_points_factory( + managed_names=['ssh', 'vault'], + supported_names=['hashivault-kv-oidc', 'hashivault-ssh-oidc'], + ) + with override_settings(FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED=True): + with mock.patch('awx.main.models.credential.detect_server_product_name', return_value='NOT_AWX'): + with mock.patch('awx.main.models.credential.entry_points', side_effect=mock_eps): + load_credentials() + + for ns in OIDC_CREDENTIAL_TYPE_NAMESPACES: + assert ns in ManagedCredentialType.registry, f"{ns} should be in registry when flag is enabled" + assert 'ssh' in ManagedCredentialType.registry + assert 'vault' in ManagedCredentialType.registry + + +def test_oidc_types_not_in_registry_when_flag_disabled(isolated_registry): + """Test that OIDC credential types are excluded from the registry when flag is disabled.""" + mock_eps = _mock_entry_points_factory( + managed_names=['ssh', 'vault'], + supported_names=['hashivault-kv-oidc', 'hashivault-ssh-oidc'], + ) + with override_settings(FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED=False): + with mock.patch('awx.main.models.credential.detect_server_product_name', return_value='NOT_AWX'): + with mock.patch('awx.main.models.credential.entry_points', side_effect=mock_eps): + load_credentials() + + for ns in OIDC_CREDENTIAL_TYPE_NAMESPACES: + assert ns not in ManagedCredentialType.registry, f"{ns} should not be in registry when flag is disabled" + # Non-OIDC types should still be loaded + assert 'ssh' in ManagedCredentialType.registry + assert 'vault' in ManagedCredentialType.registry + + +def test_oidc_namespaces_constant(): + """Test that OIDC_CREDENTIAL_TYPE_NAMESPACES contains the expected namespaces.""" + assert 'hashivault-kv-oidc' in OIDC_CREDENTIAL_TYPE_NAMESPACES + assert 'hashivault-ssh-oidc' in OIDC_CREDENTIAL_TYPE_NAMESPACES + assert len(OIDC_CREDENTIAL_TYPE_NAMESPACES) == 2 + + +# --- Functional API tests --- + + +@pytest.mark.django_db +def test_oidc_types_loaded_when_flag_enabled(get, admin, reload_credentials_with_flag): + """Test that OIDC credential types are visible in the API when flag is enabled.""" + reload_credentials_with_flag(flag_enabled=True) + + response = get(reverse('api:credential_type_list'), admin) + assert response.status_code == 200 + + namespaces = [ct['namespace'] for ct in response.data['results']] + assert 'hashivault-kv-oidc' in namespaces + assert 'hashivault-ssh-oidc' in namespaces + + +@pytest.mark.django_db +def test_oidc_types_not_loaded_when_flag_disabled(get, admin, reload_credentials_with_flag): + """Test that OIDC credential types are not visible in the API when flag is disabled.""" + reload_credentials_with_flag(flag_enabled=False) + + response = get(reverse('api:credential_type_list'), admin) + assert response.status_code == 200 + + namespaces = [ct['namespace'] for ct in response.data['results']] + assert 'hashivault-kv-oidc' not in namespaces + assert 'hashivault-ssh-oidc' not in namespaces + + # Verify they're also not in the database + assert not CredentialType.objects.filter(namespace='hashivault-kv-oidc').exists() + assert not CredentialType.objects.filter(namespace='hashivault-ssh-oidc').exists() diff --git a/awx/main/tests/functional/api/test_organizations.py b/awx/main/tests/functional/api/test_organizations.py index f86963ecc64b..af2f918ba0c5 100644 --- a/awx/main/tests/functional/api/test_organizations.py +++ b/awx/main/tests/functional/api/test_organizations.py @@ -329,3 +329,21 @@ def test_galaxy_credential_association(alice, admin, organization, post, get): 'Public Galaxy 4', 'Public Galaxy 5', ] + + +@pytest.mark.django_db +def test_org_admin_credential_count(org_admin, admin, organization, post, get): + galaxy = CredentialType.defaults['galaxy_api_token']() + galaxy.save() + + for i in range(3): + cred = Credential.objects.create(credential_type=galaxy, name=f'test_{i}', inputs={'url': 'https://galaxy.ansible.com/'}) + url = reverse('api:organization_galaxy_credentials_list', kwargs={'pk': organization.pk}) + post(url, {'associate': True, 'id': cred.pk}, user=admin, expect=204) + # org admin should see all associated galaxy credentials + resp = get(url, user=org_admin) + assert resp.data['count'] == 3 + # removing one to validate new count + post(url, {'disassociate': True, 'id': Credential.objects.get(name='test_1').pk}, user=admin, expect=204) + resp_new = get(url, user=org_admin) + assert resp_new.data['count'] == 2 diff --git a/awx/main/tests/functional/api/test_rbac_displays.py b/awx/main/tests/functional/api/test_rbac_displays.py index 8178da672c05..22d9ec9990a9 100644 --- a/awx/main/tests/functional/api/test_rbac_displays.py +++ b/awx/main/tests/functional/api/test_rbac_displays.py @@ -165,8 +165,8 @@ def _assert_one_in_list(self, data, sublist='direct_access'): def test_access_list_direct_access_capability(self, inventory, rando, get, mocker, mock_access_method): inventory.admin_role.members.add(rando) - with mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method): - response = get(reverse('api:inventory_access_list', kwargs={'pk': inventory.id}), rando) + mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method) + response = get(reverse('api:inventory_access_list', kwargs={'pk': inventory.id}), rando) mock_access_method.assert_called_once_with(inventory.admin_role, rando, 'members', **self.extra_kwargs) self._assert_one_in_list(response.data) @@ -174,8 +174,8 @@ def test_access_list_direct_access_capability(self, inventory, rando, get, mocke assert direct_access_list[0]['role']['user_capabilities']['unattach'] == 'foobar' def test_access_list_indirect_access_capability(self, inventory, organization, org_admin, get, mocker, mock_access_method): - with mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method): - response = get(reverse('api:inventory_access_list', kwargs={'pk': inventory.id}), org_admin) + mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method) + response = get(reverse('api:inventory_access_list', kwargs={'pk': inventory.id}), org_admin) mock_access_method.assert_called_once_with(organization.admin_role, org_admin, 'members', **self.extra_kwargs) self._assert_one_in_list(response.data, sublist='indirect_access') @@ -185,8 +185,8 @@ def test_access_list_indirect_access_capability(self, inventory, organization, o def test_access_list_team_direct_access_capability(self, inventory, team, team_member, get, mocker, mock_access_method): team.member_role.children.add(inventory.admin_role) - with mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method): - response = get(reverse('api:inventory_access_list', kwargs={'pk': inventory.id}), team_member) + mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method) + response = get(reverse('api:inventory_access_list', kwargs={'pk': inventory.id}), team_member) mock_access_method.assert_called_once_with(inventory.admin_role, team.member_role, 'parents', **self.extra_kwargs) self._assert_one_in_list(response.data) @@ -198,8 +198,8 @@ def test_access_list_team_direct_access_capability(self, inventory, team, team_m def test_team_roles_unattach(mocker, team, team_member, inventory, mock_access_method, get): team.member_role.children.add(inventory.admin_role) - with mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method): - response = get(reverse('api:team_roles_list', kwargs={'pk': team.id}), team_member) + mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method) + response = get(reverse('api:team_roles_list', kwargs={'pk': team.id}), team_member) # Did we assess whether team_member can remove team's permission to the inventory? mock_access_method.assert_called_once_with(inventory.admin_role, team.member_role, 'parents', skip_sub_obj_read_check=True, data={}) @@ -212,8 +212,8 @@ def test_user_roles_unattach(mocker, organization, alice, bob, mock_access_metho organization.member_role.members.add(alice) organization.member_role.members.add(bob) - with mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method): - response = get(reverse('api:user_roles_list', kwargs={'pk': alice.id}), bob) + mocker.patch.object(access_registry[Role], 'can_unattach', mock_access_method) + response = get(reverse('api:user_roles_list', kwargs={'pk': alice.id}), bob) # Did we assess whether bob can remove alice's permission to the inventory? mock_access_method.assert_called_once_with(organization.member_role, alice, 'members', skip_sub_obj_read_check=True, data={}) diff --git a/awx/main/tests/functional/api/test_role.py b/awx/main/tests/functional/api/test_role.py index cec31d9d7ede..68ce8855feab 100644 --- a/awx/main/tests/functional/api/test_role.py +++ b/awx/main/tests/functional/api/test_role.py @@ -3,17 +3,6 @@ from awx.api.versioning import reverse -@pytest.mark.django_db -def test_admin_visible_to_orphaned_users(get, alice): - names = set() - - response = get(reverse('api:role_list'), user=alice) - for item in response.data['results']: - names.add(item['name']) - assert 'System Auditor' in names - assert 'System Administrator' in names - - @pytest.mark.django_db @pytest.mark.parametrize('role,code', [('member_role', 400), ('admin_role', 400), ('inventory_admin_role', 204)]) @pytest.mark.parametrize('reversed', [True, False]) diff --git a/awx/main/tests/functional/api/test_schedules.py b/awx/main/tests/functional/api/test_schedules.py index c1cd0779c35f..cb49a53d22a5 100644 --- a/awx/main/tests/functional/api/test_schedules.py +++ b/awx/main/tests/functional/api/test_schedules.py @@ -8,7 +8,6 @@ from awx.main.models import JobTemplate, Schedule from awx.main.utils.encryption import decrypt_value, get_encryption_key - RRULE_EXAMPLE = 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1' diff --git a/awx/main/tests/functional/api/test_serializers.py b/awx/main/tests/functional/api/test_serializers.py new file mode 100644 index 000000000000..ab31e186e9c4 --- /dev/null +++ b/awx/main/tests/functional/api/test_serializers.py @@ -0,0 +1,75 @@ +import pytest + +from django.test.utils import override_settings + +from rest_framework.serializers import ValidationError + +from awx.api.serializers import UserSerializer +from django.contrib.auth.models import User + + +@pytest.mark.parametrize( + "password,min_length,min_digits,min_upper,min_special,expect_error", + [ + # Test length + ("a", 1, 0, 0, 0, False), + ("a", 2, 0, 0, 0, True), + ("aa", 2, 0, 0, 0, False), + ("aaabcDEF123$%^", 2, 0, 0, 0, False), + # Test digits + ("a", 0, 1, 0, 0, True), + ("1", 0, 1, 0, 0, False), + ("1", 0, 2, 0, 0, True), + ("12", 0, 2, 0, 0, False), + ("12abcDEF123$%^", 0, 2, 0, 0, False), + # Test upper + ("a", 0, 0, 1, 0, True), + ("A", 0, 0, 1, 0, False), + ("A", 0, 0, 2, 0, True), + ("AB", 0, 0, 2, 0, False), + ("ABabcDEF123$%^", 0, 0, 2, 0, False), + # Test special + ("a", 0, 0, 0, 1, True), + ("!", 0, 0, 0, 1, False), + ("!", 0, 0, 0, 2, True), + ("!@", 0, 0, 0, 2, False), + ("!@abcDEF123$%^", 0, 0, 0, 2, False), + ], +) +@pytest.mark.django_db +def test_validate_password_rules(password, min_length, min_digits, min_upper, min_special, expect_error): + user_serializer = UserSerializer() + + # First test password with no params, this should always pass + try: + user_serializer.validate_password(password) + except ValidationError: + assert False, f"Password {password} should not have validation issue if no params are used" + + with override_settings( + LOCAL_PASSWORD_MIN_LENGTH=min_length, LOCAL_PASSWORD_MIN_DIGITS=min_digits, LOCAL_PASSWORD_MIN_UPPER=min_upper, LOCAL_PASSWORD_MIN_SPECIAL=min_special + ): + if expect_error: + with pytest.raises(ValidationError): + user_serializer.validate_password(password) + else: + try: + user_serializer.validate_password(password) + except ValidationError: + assert False, "validate_password raised an unexpected exception" + + +@pytest.mark.django_db +def test_validate_password_too_long(): + password_max_length = User._meta.get_field('password').max_length + password = "x" * password_max_length + + user_serializer = UserSerializer() + try: + user_serializer.validate_password(password) + except ValidationError: + assert False, f"Password {password} should not have validation" + + password = f"{password}x" + with pytest.raises(ValidationError): + user_serializer.validate_password(password) diff --git a/awx/main/tests/functional/api/test_settings.py b/awx/main/tests/functional/api/test_settings.py index a1ae7398a5bf..e096637dc28c 100644 --- a/awx/main/tests/functional/api/test_settings.py +++ b/awx/main/tests/functional/api/test_settings.py @@ -7,10 +7,8 @@ # AWX from awx.api.versioning import reverse -from awx.conf.models import Setting from awx.conf.registry import settings_registry - TEST_GIF_LOGO = 'data:image/gif;base64,R0lGODlhIQAjAPIAAP//////AP8AAMzMAJmZADNmAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCgAHACwAAAAAIQAjAAADo3i63P4wykmrvTjrzZsxXfR94WMQBFh6RECuixHMLyzPQ13ewZCvow9OpzEAjIBj79cJJmU+FceIVEZ3QRozxBttmyOBwPBtisdX4Bha3oxmS+llFIPHQXQKkiSEXz9PeklHBzx3hYNyEHt4fmmAhHp8Nz45KgV5FgWFOFEGmwWbGqEfniChohmoQZ+oqRiZDZhEgk81I4mwg4EKVbxzrDHBEAkAIfkECQoABwAsAAAAACEAIwAAA6V4utz+MMpJq724GpP15p1kEAQYQmOwnWjgrmxjuMEAx8rsDjZ+fJvdLWQAFAHGWo8FRM54JqIRmYTigDrDMqZTbbbMj0CgjTLHZKvPQH6CTx+a2vKR0XbbOsoZ7SphG057gjl+c0dGgzeGNiaBiSgbBQUHBV08NpOVlkMSk0FKjZuURHiiOJxQnSGfQJuoEKREejK0dFRGjoiQt7iOuLx0rgxYEQkAIfkECQoABwAsAAAAACEAIwAAA7h4utxnxslJDSGR6nrz/owxYB64QUEwlGaVqlB7vrAJscsd3Lhy+wBArGEICo3DUFH4QDqK0GMy51xOgcGlEAfJ+iAFie62chR+jYKaSAuQGOqwJp7jGQRDuol+F/jxZWsyCmoQfwYwgoM5Oyg1i2w0A2WQIW2TPYOIkleQmy+UlYygoaIPnJmapKmqKiusMmSdpjxypnALtrcHioq3ury7hGm3dnVosVpMWFmwREZbddDOSsjVswcJACH5BAkKAAcALAAAAAAhACMAAAOxeLrc/jDKSZUxNS9DCNYV54HURQwfGRlDEFwqdLVuGjOsW9/Odb0wnsUAKBKNwsMFQGwyNUHckVl8bqI4o43lA26PNkv1S9DtNuOeVirw+aTI3qWAQwnud1vhLSnQLS0GeFF+GoVKNF0fh4Z+LDQ6Bn5/MTNmL0mAl2E3j2aclTmRmYCQoKEDiaRDKFhJez6UmbKyQowHtzy1uEl8DLCnEktrQ2PBD1NxSlXKIW5hz6cJACH5BAkKAAcALAAAAAAhACMAAAOkeLrc/jDKSau9OOvNlTFd9H3hYxAEWDJfkK5LGwTq+g0zDR/GgM+10A04Cm56OANgqTRmkDTmSOiLMgFOTM9AnFJHuexzYBAIijZf2SweJ8ttbbXLmd5+wBiJosSCoGF/fXEeS1g8gHl9hxODKkh4gkwVIwUekESIhA4FlgV3PyCWG52WI2oGnR2lnUWpqhqVEF4Xi7QjhpsshpOFvLosrnpoEAkAIfkECQoABwAsAAAAACEAIwAAA6l4utz+MMpJq71YGpPr3t1kEAQXQltQnk8aBCa7bMMLy4wx1G8s072PL6SrGQDI4zBThCU/v50zCVhidIYgNPqxWZkDg0AgxB2K4vEXbBSvr1JtZ3uOext0x7FqovF6OXtfe1UzdjAxhINPM013ChtJER8FBQeVRX8GlpggFZWWfjwblTiigGZnfqRmpUKbljKxDrNMeY2eF4R8jUiSur6/Z8GFV2WBtwwJACH5BAkKAAcALAAAAAAhACMAAAO6eLrcZi3KyQwhkGpq8f6ONWQgaAxB8JTfg6YkO50pzD5xhaurhCsGAKCnEw6NucNDCAkyI8ugdAhFKpnJJdMaeiofBejowUseCr9GYa0j1GyMdVgjBxoEuPSZXWKf7gKBeHtzMms0gHgGfDIVLztmjScvNZEyk28qjT40b5aXlHCbDgOhnzedoqOOlKeopaqrCy56sgtotbYKhYW6e7e9tsHBssO6eSTIm1peV0iuFUZDyU7NJnmcuQsJACH5BAkKAAcALAAAAAAhACMAAAOteLrc/jDKSZsxNS9DCNYV54Hh4H0kdAXBgKaOwbYX/Miza1vrVe8KA2AoJL5gwiQgeZz4GMXlcHl8xozQ3kW3KTajL9zsBJ1+sV2fQfALem+XAlRApxu4ioI1UpC76zJ4fRqDBzI+LFyFhH1iiS59fkgziW07jjRAG5QDeECOLk2Tj6KjnZafW6hAej6Smgevr6yysza2tiCuMasUF2Yov2gZUUQbU8YaaqjLpQkAOw==' # NOQA TEST_PNG_LOGO = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAjCAYAAAAaLGNkAAAAAXNSR0IB2cksfwAAAdVpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpDb21wcmVzc2lvbj4xPC90aWZmOkNvbXByZXNzaW9uPgogICAgICAgICA8dGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPjI8L3RpZmY6UGhvdG9tZXRyaWNJbnRlcnByZXRhdGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cjl0tmoAAAHVSURBVFgJ7VZRsoMgDNTOu5E9U+/Ud6Z6JssGNg2oNKD90xkHCNnNkgTbYbieKwNXBn6bgSXQ4+16xi5UDiqDN3Pecr6+1fM5DHh7n1NEIPjjoRLKzOjG3qQ5dRtEy2LCjh/Gz2wDZE2nZYKkrxdn/kY9XQQkGCGqqDY5IgJFkEKgBCzDNGXhTKEye7boFRH6IPJj5EshiNCSjV4R4eSx7zhmR2tcdIuwmWiMeao7e0JHViZEWUI5aP8a9O+rx74D6sGEiJftiX3YeueIiFXg2KrhpqzjVC3dPZFYJZ7NOwwtNwM8R0UkLfH0sT5qck+OlkMq0BucKr0iWG7gpAQksD9esM1z3Lnf6SHjLh67nnKEGxC/iomWhByTeXOQJGHHcKxwHhHKnt1HIdYtmexkIb/HOURWTSJqn2gKMDG0bDUc/D0iAseovxUBoylmQCug6IVhSv+4DIeKI94jAr4AjiSEgQ25JYB+YWT9BZ94AM8erwgFkRifaArA6U0G5KT0m//z26REZuK9okgrT6VwE1jTHjbVzyNAyRwTEPOtuiex9FVBNZCkruaA4PZqFp1u8Rpww9/6rcK5y0EkAxRiZJt79PWOVYWGRE9pbJhavMengMflGyumk0akMsQnAAAAAElFTkSuQmCC' # NOQA TEST_JPEG_LOGO = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBkRXhpZgAATU0AKgAAAAgAAwEGAAMAAAABAAIAAAESAAMAAAABAAEAAIdpAAQAAAABAAAAMgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIaADAAQAAAABAAAAIwAAAAD/4QkhaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiLz4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+AP/tADhQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAADhCSU0EJQAAAAAAENQdjNmPALIE6YAJmOz4Qn7/wAARCAAjACEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwAGBgYGBgYKBgYKDgoKCg4SDg4ODhIXEhISEhIXHBcXFxcXFxwcHBwcHBwcIiIiIiIiJycnJycsLCwsLCwsLCws/9sAQwEHBwcLCgsTCgoTLh8aHy4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4u/90ABAAD/9oADAMBAAIRAxEAPwD6poormvFfivSvB2lHVtWLGMtsRE2hnYKzlVLsi52oxALDdjauWKqQCXQfFXh7xP8Aaf7AvYrz7HL5U3lk/K3YjIGVODtcZVsHBODXQV806bcT+E9L03XbCOS2udMsLQanbB4po72xYMfOQpKYyV2zPEwcNwVK7WAr6WriwWMWIUvdcZRdmnuu33rVFSjYKKKK7ST/0PqmuF8Vv4X8S+HNZ0+e/gIsYJvtEsL+bJZsI3UuyxNvBA3gpxvXchyCRXdV8ta3bW667DoloW1y10tLLTJxZWP2hoLSGYzNHclGZpJC0ESk8IAZcRB8is61T2cHK1/1DrY526h8YXHh691vxCz6dafY5Q0U7yGSeQxSxohNzJLcbUeQ4VnVNxBRCWL19b2eraVqE9xa2F3BcS2jbJ0ikV2ibJG1wpJU5UjBx0PpXzrrniy4k17TrrWrGex022ufMijvd9m11PGH8naXKqsUcgR3MhB5U7MA16x4L8F3vhq2sY9Ru4rg6day2tusEAhCrcOkknmEMRI2Y1AcLGT8xYMzZHjZFGu6cquKjaUnt2XS76vv/SN8RVjOdoKyXY9Cooor3TA//9H6pr4gfxRrMvxJ0/whLJE+maVrcVnZRtBCzwQQ3SIipMU80fKignflgPmJr7fr4A/5rf8A9zJ/7eUAdX8SfGviPwl8TtaPh6eK1eTyN0n2eCSUg28OV8ySNn2/KDtztzzjNfZVhY2umWMGm2KeXb2sSQxJknakYCqMkknAHUnNfBXxt/5Kdq//AG7/APpPFX3/AEAFFFFAH//Z' # NOQA @@ -67,129 +65,6 @@ def test_awx_task_env_validity(get, patch, admin, value, expected): assert resp.data['AWX_TASK_ENV'] == dict() -@pytest.mark.django_db -def test_ldap_settings(get, put, patch, delete, admin): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ldap'}) - get(url, user=admin, expect=200) - # The PUT below will fail at the moment because AUTH_LDAP_GROUP_TYPE - # defaults to None but cannot be set to None. - # put(url, user=admin, data=response.data, expect=200) - delete(url, user=admin, expect=204) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': ''}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldap.example.com'}, expect=400) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldap://ldap.example.com'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldaps://ldap.example.com'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldap://ldap.example.com:389'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldaps://ldap.example.com:636'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldap://ldap.example.com ldap://ldap2.example.com'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldap://ldap.example.com,ldap://ldap2.example.com'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_SERVER_URI': 'ldap://ldap.example.com, ldap://ldap2.example.com'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_BIND_DN': 'cn=Manager,dc=example,dc=com'}, expect=200) - patch(url, user=admin, data={'AUTH_LDAP_BIND_DN': u'cn=暴力膜,dc=大新闻,dc=真的粉丝'}, expect=200) - - -@pytest.mark.django_db -@pytest.mark.parametrize( - 'value', - [ - None, - '', - 'INVALID', - 1, - [1], - ['INVALID'], - ], -) -def test_ldap_user_flags_by_group_invalid_dn(get, patch, admin, value): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ldap'}) - patch(url, user=admin, data={'AUTH_LDAP_USER_FLAGS_BY_GROUP': {'is_superuser': value}}, expect=400) - - -@pytest.mark.django_db -def test_ldap_user_flags_by_group_string(get, patch, admin): - expected = 'CN=Admins,OU=Groups,DC=example,DC=com' - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ldap'}) - patch(url, user=admin, data={'AUTH_LDAP_USER_FLAGS_BY_GROUP': {'is_superuser': expected}}, expect=200) - resp = get(url, user=admin) - assert resp.data['AUTH_LDAP_USER_FLAGS_BY_GROUP']['is_superuser'] == [expected] - - -@pytest.mark.django_db -def test_ldap_user_flags_by_group_list(get, patch, admin): - expected = ['CN=Admins,OU=Groups,DC=example,DC=com', 'CN=Superadmins,OU=Groups,DC=example,DC=com'] - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ldap'}) - patch(url, user=admin, data={'AUTH_LDAP_USER_FLAGS_BY_GROUP': {'is_superuser': expected}}, expect=200) - resp = get(url, user=admin) - assert resp.data['AUTH_LDAP_USER_FLAGS_BY_GROUP']['is_superuser'] == expected - - -@pytest.mark.parametrize( - 'setting', - [ - 'AUTH_LDAP_USER_DN_TEMPLATE', - 'AUTH_LDAP_REQUIRE_GROUP', - 'AUTH_LDAP_DENY_GROUP', - ], -) -@pytest.mark.django_db -def test_empty_ldap_dn(get, put, patch, delete, admin, setting): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ldap'}) - patch(url, user=admin, data={setting: ''}, expect=200) - resp = get(url, user=admin, expect=200) - assert resp.data[setting] is None - - patch(url, user=admin, data={setting: None}, expect=200) - resp = get(url, user=admin, expect=200) - assert resp.data[setting] is None - - -@pytest.mark.django_db -def test_radius_settings(get, put, patch, delete, admin, settings): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'radius'}) - response = get(url, user=admin, expect=200) - put(url, user=admin, data=response.data, expect=200) - # Set secret via the API. - patch(url, user=admin, data={'RADIUS_SECRET': 'mysecret'}, expect=200) - response = get(url, user=admin, expect=200) - assert response.data['RADIUS_SECRET'] == '$encrypted$' - assert Setting.objects.filter(key='RADIUS_SECRET').first().value.startswith('$encrypted$') - assert settings.RADIUS_SECRET == 'mysecret' - # Set secret via settings wrapper. - settings_wrapper = settings._awx_conf_settings - settings_wrapper.RADIUS_SECRET = 'mysecret2' - response = get(url, user=admin, expect=200) - assert response.data['RADIUS_SECRET'] == '$encrypted$' - assert Setting.objects.filter(key='RADIUS_SECRET').first().value.startswith('$encrypted$') - assert settings.RADIUS_SECRET == 'mysecret2' - # If we send back $encrypted$, the setting is not updated. - patch(url, user=admin, data={'RADIUS_SECRET': '$encrypted$'}, expect=200) - response = get(url, user=admin, expect=200) - assert response.data['RADIUS_SECRET'] == '$encrypted$' - assert Setting.objects.filter(key='RADIUS_SECRET').first().value.startswith('$encrypted$') - assert settings.RADIUS_SECRET == 'mysecret2' - # If we send an empty string, the setting is also set to an empty string. - patch(url, user=admin, data={'RADIUS_SECRET': ''}, expect=200) - response = get(url, user=admin, expect=200) - assert response.data['RADIUS_SECRET'] == '' - assert Setting.objects.filter(key='RADIUS_SECRET').first().value == '' - assert settings.RADIUS_SECRET == '' - - -@pytest.mark.django_db -def test_tacacsplus_settings(get, put, patch, admin): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'tacacsplus'}) - response = get(url, user=admin, expect=200) - put(url, user=admin, data=response.data, expect=200) - patch(url, user=admin, data={'TACACSPLUS_SECRET': 'mysecret'}, expect=200) - patch(url, user=admin, data={'TACACSPLUS_SECRET': ''}, expect=200) - patch(url, user=admin, data={'TACACSPLUS_HOST': 'localhost'}, expect=400) - patch(url, user=admin, data={'TACACSPLUS_SECRET': 'mysecret'}, expect=200) - patch(url, user=admin, data={'TACACSPLUS_HOST': 'localhost'}, expect=200) - patch(url, user=admin, data={'TACACSPLUS_HOST': '', 'TACACSPLUS_SECRET': ''}, expect=200) - patch(url, user=admin, data={'TACACSPLUS_HOST': 'localhost', 'TACACSPLUS_SECRET': ''}, expect=400) - patch(url, user=admin, data={'TACACSPLUS_HOST': 'localhost', 'TACACSPLUS_SECRET': 'mysecret'}, expect=200) - - @pytest.mark.django_db def test_ui_settings(get, put, patch, delete, admin): url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ui'}) @@ -318,76 +193,3 @@ def test_logging_aggregator_connection_test_valid(put, post, admin): # "Test" the logger url = reverse('api:setting_logging_test') post(url, {}, user=admin, expect=202) - - -@pytest.mark.django_db -@pytest.mark.parametrize('headers', [True, False]) -def test_saml_x509cert_validation(patch, get, admin, headers): - cert = "MIIEogIBAAKCAQEA1T4za6qBbHxFpN5f9eFvA74MFjrsjcp1uvzOaE23AYKMDEJghJ6dqQ7GwHLNIeIeumqDFmODauIzrgSDJTT5+NG30Rr+rRi0zDkrkBAj/AtA+SaVhbzqB6ZSd7LaMly9XAc+82OKlNpuWS9hPmFaSShzDTXRu5RRyvm4NDCAOGDu5hyVR2pV/ffKDNfNkChnqzvRRW9laQcVmliZhlTGn7nPZ+JbjpwEy0nwW+4zoAiEvwnT52N4xTqIcYOnXtGiaf13dh7FkUfYmS0tzF3+h8QRKwtIm4y+sq84R/kr79/0t5aRUpJynNrECajzmArpL4IjXKTPIyUpTKirJgGnCwIDAQABAoIBAC6bbbm2hpsjfkVOpUKkhxMWUqX5MwK6oYjBAIwjkEAwPFPhnh7eXC87H42oidVCCt1LsmMOVQbjcdAzBEb5kTkk/Twi3k8O+1U3maHfJT5NZ2INYNjeNXh+jb/Dw5UGWAzpOIUR2JQ4Oa4cgPCVbppW0O6uOKz6+fWXJv+hKiUoBCC0TiY52iseHJdUOaKNxYRD2IyIzCAxFSd5tZRaARIYDsugXp3E/TdbsVWA7bmjIBOXq+SquTrlB8x7j3B7+Pi09nAJ2U/uV4PHE+/2Fl009ywfmqancvnhwnz+GQ5jjP+gTfghJfbO+Z6M346rS0Vw+osrPgfyudNHlCswHOECgYEA/Cfq25gDP07wo6+wYWbx6LIzj/SSZy/Ux9P8zghQfoZiPoaq7BQBPAzwLNt7JWST8U11LZA8/wo6ch+HSTMk+m5ieVuru2cHxTDqeNlh94eCrNwPJ5ayA5U6LxAuSCTAzp+rv6KQUx1JcKSEHuh+nRYTKvUDE6iA6YtPLO96lLUCgYEA2H5rOPX2M4w1Q9zjol77lplbPRdczXNd0PIzhy8Z2ID65qvmr1nxBG4f2H96ykW8CKLXNvSXreNZ1BhOXc/3Hv+3mm46iitB33gDX4mlV4Jyo/w5IWhUKRyoW6qXquFFsScxRzTrx/9M+aZeRRLdsBk27HavFEg6jrbQ0SleZL8CgYAaM6Op8d/UgkVrHOR9Go9kmK/W85kK8+NuaE7Ksf57R0eKK8AzC9kc/lMuthfTyOG+n0ff1i8gaVWtai1Ko+/hvfqplacAsDIUgYK70AroB8LCZ5ODj5sr2CPVpB7LDFakod7c6O2KVW6+L7oy5AHUHOkc+5y4PDg5DGrLxo68SQKBgAlGoWF3aG0c/MtDk51JZI43U+lyLs++ua5SMlMAeaMFI7rucpvgxqrh7Qthqukvw7a7A22fXUBeFWM5B2KNnpD9c+hyAKAa6l+gzMQzKZpuRGsyS2BbEAAS8kO7M3Rm4o2MmFfstI2FKs8nibJ79HOvIONQ0n+T+K5Utu2/UAQRAoGAFB4fiIyQ0nYzCf18Z4Wvi/qeIOW+UoBonIN3y1h4wruBywINHxFMHx4aVImJ6R09hoJ9D3Mxli3xF/8JIjfTG5fBSGrGnuofl14d/XtRDXbT2uhVXrIkeLL/ojODwwEx0VhxIRUEjPTvEl6AFSRRcBp3KKzQ/cu7ENDY6GTlOUI=" # noqa - if headers: - cert = '-----BEGIN CERTIFICATE-----\n' + cert + '\n-----END CERTIFICATE-----' - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'saml'}) - resp = patch( - url, - user=admin, - data={ - 'SOCIAL_AUTH_SAML_ENABLED_IDPS': { - "okta": { - "attr_last_name": "LastName", - "attr_username": "login", - "entity_id": "http://www.okta.com/abc123", - "attr_user_permanent_id": "login", - "url": "https://example.okta.com/app/abc123/xyz123/sso/saml", - "attr_email": "Email", - "x509cert": cert, - "attr_first_name": "FirstName", - } - } - }, - ) - assert resp.status_code == 200 - - -@pytest.mark.django_db -def test_github_settings(get, put, patch, delete, admin): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'github'}) - get(url, user=admin, expect=200) - delete(url, user=admin, expect=204) - response = get(url, user=admin, expect=200) - data = dict(response.data.items()) - put(url, user=admin, data=data, expect=200) - patch(url, user=admin, data={'SOCIAL_AUTH_GITHUB_KEY': '???'}, expect=200) - response = get(url, user=admin, expect=200) - assert response.data['SOCIAL_AUTH_GITHUB_KEY'] == '???' - data.pop('SOCIAL_AUTH_GITHUB_KEY') - put(url, user=admin, data=data, expect=200) - response = get(url, user=admin, expect=200) - assert response.data['SOCIAL_AUTH_GITHUB_KEY'] == '' - - -@pytest.mark.django_db -def test_github_enterprise_settings(get, put, patch, delete, admin): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'github-enterprise'}) - get(url, user=admin, expect=200) - delete(url, user=admin, expect=204) - response = get(url, user=admin, expect=200) - data = dict(response.data.items()) - put(url, user=admin, data=data, expect=200) - patch( - url, - user=admin, - data={ - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'example.com', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'example.com', - }, - expect=200, - ) - response = get(url, user=admin, expect=200) - assert response.data['SOCIAL_AUTH_GITHUB_ENTERPRISE_URL'] == 'example.com' - assert response.data['SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL'] == 'example.com' - data.pop('SOCIAL_AUTH_GITHUB_ENTERPRISE_URL') - data.pop('SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL') - put(url, user=admin, data=data, expect=200) - response = get(url, user=admin, expect=200) - assert response.data['SOCIAL_AUTH_GITHUB_ENTERPRISE_URL'] == '' - assert response.data['SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL'] == '' diff --git a/awx/main/tests/functional/api/test_survey_spec.py b/awx/main/tests/functional/api/test_survey_spec.py index cbb22b3bdcce..ec20806f6bfd 100644 --- a/awx/main/tests/functional/api/test_survey_spec.py +++ b/awx/main/tests/functional/api/test_survey_spec.py @@ -2,12 +2,12 @@ import pytest import json +from ansible_base.lib.utils.models import get_type_for_model from awx.api.versioning import reverse from awx.main.models.jobs import JobTemplate, Job from awx.main.models.activity_stream import ActivityStream from awx.main.access import JobTemplateAccess -from awx.main.utils.common import get_type_for_model @pytest.fixture diff --git a/awx/main/tests/functional/api/test_unified_job_template.py b/awx/main/tests/functional/api/test_unified_job_template.py index 1a9adc396583..c293827e437b 100644 --- a/awx/main/tests/functional/api/test_unified_job_template.py +++ b/awx/main/tests/functional/api/test_unified_job_template.py @@ -18,7 +18,7 @@ class TestUnifiedOrganization: def data_for_model(self, model, orm_style=False): data = {'name': 'foo', 'organization': None} if model == 'JobTemplate': - proj = models.Project.objects.create(name="test-proj", playbook_files=['helloworld.yml']) + proj = models.Project.objects.create(name="test-proj", playbook_files=['helloworld.yml'], scm_type='git', scm_url='https://foo.invalid') if orm_style: data['project_id'] = proj.id else: diff --git a/awx/main/tests/functional/api/test_unified_jobs_stdout.py b/awx/main/tests/functional/api/test_unified_jobs_stdout.py index dad55c5ba061..3565a73d77a7 100644 --- a/awx/main/tests/functional/api/test_unified_jobs_stdout.py +++ b/awx/main/tests/functional/api/test_unified_jobs_stdout.py @@ -57,7 +57,7 @@ def _mk_inventory_update(created=None): [_mk_inventory_update, InventoryUpdateEvent, 'inventory_update', 'api:inventory_update_stdout'], ], ) -def test_text_stdout(sqlite_copy_expert, Parent, Child, relation, view, get, admin): +def test_text_stdout(sqlite_copy, Parent, Child, relation, view, get, admin): job = Parent() job.save() for i in range(3): @@ -79,11 +79,11 @@ def test_text_stdout(sqlite_copy_expert, Parent, Child, relation, view, get, adm ], ) @pytest.mark.parametrize('download', [True, False]) -def test_ansi_stdout_filtering(sqlite_copy_expert, Parent, Child, relation, view, download, get, admin): +def test_ansi_stdout_filtering(sqlite_copy, Parent, Child, relation, view, download, get, admin): job = Parent() job.save() for i in range(3): - Child(**{relation: job, 'stdout': '\x1B[0;36mTesting {}\x1B[0m\n'.format(i), 'start_line': i}).save() + Child(**{relation: job, 'stdout': '\x1b[0;36mTesting {}\x1b[0m\n'.format(i), 'start_line': i}).save() url = reverse(view, kwargs={'pk': job.pk}) # ansi codes in ?format=txt should get filtered @@ -96,7 +96,7 @@ def test_ansi_stdout_filtering(sqlite_copy_expert, Parent, Child, relation, view # ask for ansi and you'll get it fmt = "?format={}".format("ansi_download" if download else "ansi") response = get(url + fmt, user=admin, expect=200) - assert smart_str(response.content).splitlines() == ['\x1B[0;36mTesting %d\x1B[0m' % i for i in range(3)] + assert smart_str(response.content).splitlines() == ['\x1b[0;36mTesting %d\x1b[0m' % i for i in range(3)] has_download_header = response.has_header('Content-Disposition') assert has_download_header if download else not has_download_header @@ -111,11 +111,11 @@ def test_ansi_stdout_filtering(sqlite_copy_expert, Parent, Child, relation, view [_mk_inventory_update, InventoryUpdateEvent, 'inventory_update', 'api:inventory_update_stdout'], ], ) -def test_colorized_html_stdout(sqlite_copy_expert, Parent, Child, relation, view, get, admin): +def test_colorized_html_stdout(sqlite_copy, Parent, Child, relation, view, get, admin): job = Parent() job.save() for i in range(3): - Child(**{relation: job, 'stdout': '\x1B[0;36mTesting {}\x1B[0m\n'.format(i), 'start_line': i}).save() + Child(**{relation: job, 'stdout': '\x1b[0;36mTesting {}\x1b[0m\n'.format(i), 'start_line': i}).save() url = reverse(view, kwargs={'pk': job.pk}) + '?format=html' response = get(url, user=admin, expect=200) @@ -134,7 +134,7 @@ def test_colorized_html_stdout(sqlite_copy_expert, Parent, Child, relation, view [_mk_inventory_update, InventoryUpdateEvent, 'inventory_update', 'api:inventory_update_stdout'], ], ) -def test_stdout_line_range(sqlite_copy_expert, Parent, Child, relation, view, get, admin): +def test_stdout_line_range(sqlite_copy, Parent, Child, relation, view, get, admin): job = Parent() job.save() for i in range(20): @@ -146,7 +146,7 @@ def test_stdout_line_range(sqlite_copy_expert, Parent, Child, relation, view, ge @pytest.mark.django_db -def test_text_stdout_from_system_job_events(sqlite_copy_expert, get, admin): +def test_text_stdout_from_system_job_events(sqlite_copy, get, admin): created = tz_now() job = SystemJob(created=created) job.save() @@ -158,7 +158,7 @@ def test_text_stdout_from_system_job_events(sqlite_copy_expert, get, admin): @pytest.mark.django_db -def test_text_stdout_with_max_stdout(sqlite_copy_expert, get, admin): +def test_text_stdout_with_max_stdout(sqlite_copy, get, admin): created = tz_now() job = SystemJob(created=created) job.save() @@ -185,7 +185,7 @@ def test_text_stdout_with_max_stdout(sqlite_copy_expert, get, admin): ) @pytest.mark.parametrize('fmt', ['txt', 'ansi']) @mock.patch('awx.main.redact.UriCleaner.SENSITIVE_URI_PATTERN', mock.Mock(**{'search.return_value': None})) # really slow for large strings -def test_max_bytes_display(sqlite_copy_expert, Parent, Child, relation, view, fmt, get, admin): +def test_max_bytes_display(sqlite_copy, Parent, Child, relation, view, fmt, get, admin): created = tz_now() job = Parent(created=created) job.save() @@ -255,7 +255,7 @@ def test_legacy_result_stdout_with_max_bytes(Cls, view, fmt, get, admin): ], ) @pytest.mark.parametrize('fmt', ['txt', 'ansi', 'txt_download', 'ansi_download']) -def test_text_with_unicode_stdout(sqlite_copy_expert, Parent, Child, relation, view, get, admin, fmt): +def test_text_with_unicode_stdout(sqlite_copy, Parent, Child, relation, view, get, admin, fmt): job = Parent() job.save() for i in range(3): @@ -267,7 +267,7 @@ def test_text_with_unicode_stdout(sqlite_copy_expert, Parent, Child, relation, v @pytest.mark.django_db -def test_unicode_with_base64_ansi(sqlite_copy_expert, get, admin): +def test_unicode_with_base64_ansi(sqlite_copy, get, admin): created = tz_now() job = Job(created=created) job.save() diff --git a/awx/main/tests/functional/api/test_unified_jobs_view.py b/awx/main/tests/functional/api/test_unified_jobs_view.py index a8c7b53461e8..9a0955ba8012 100644 --- a/awx/main/tests/functional/api/test_unified_jobs_view.py +++ b/awx/main/tests/functional/api/test_unified_jobs_view.py @@ -7,7 +7,6 @@ from awx.main.tests.URI import URI from awx.main.constants import ACTIVE_STATES - TEST_STATES = list(ACTIVE_STATES) TEST_STATES.remove('new') diff --git a/awx/main/tests/functional/api/test_user.py b/awx/main/tests/functional/api/test_user.py index c19192c90caa..366f6f943c8d 100644 --- a/awx/main/tests/functional/api/test_user.py +++ b/awx/main/tests/functional/api/test_user.py @@ -1,14 +1,13 @@ -from datetime import date from unittest import mock import pytest from django.contrib.sessions.middleware import SessionMiddleware +from django.test.utils import override_settings from awx.main.models import User from awx.api.versioning import reverse - # # user creation # @@ -24,6 +23,175 @@ def test_user_create(post, admin): assert not response.data['is_system_auditor'] +# Disable local password checks to ensure that any ValidationError originates from the Django validators. +@override_settings( + LOCAL_PASSWORD_MIN_LENGTH=1, + LOCAL_PASSWORD_MIN_DIGITS=0, + LOCAL_PASSWORD_MIN_UPPER=0, + LOCAL_PASSWORD_MIN_SPECIAL=0, +) +@pytest.mark.django_db +def test_user_create_with_django_password_validation_basic(post, admin): + """Test if the Django password validators are applied correctly.""" + with override_settings( + AUTH_PASSWORD_VALIDATORS=[ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + 'OPTIONS': { + 'min_length': 3, + }, + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, + ], + ): + # This user should fail the UserAttrSimilarity, MinLength and CommonPassword validators. + user_attrs = ( + { + "password": "Password", # NOSONAR + "username": "Password", + "is_superuser": False, + }, + ) + print(f"Create user with invalid password {user_attrs=}") + response = post(reverse('api:user_list'), user_attrs, admin, middleware=SessionMiddleware(mock.Mock())) + assert response.status_code == 400 + # This user should pass all Django validators. + user_attrs = { + "password": "r$TyKiOCb#ED", # NOSONAR + "username": "TestUser", + "is_superuser": False, + } + print(f"Create user with valid password {user_attrs=}") + response = post(reverse('api:user_list'), user_attrs, admin, middleware=SessionMiddleware(mock.Mock())) + assert response.status_code == 201 + + +@pytest.mark.parametrize( + "user_attrs,validators,expected_status_code", + [ + # Test password similarity with username. + ( + {"password": "TestUser1", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, + ], + 400, + ), + ( + {"password": "abc", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, + ], + 201, + ), + # Test password min length criterion. + ( + {"password": "TooShort", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 9}}, + ], + 400, + ), + ( + {"password": "LongEnough", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 9}}, + ], + 201, + ), + # Test password is too common criterion. + ( + {"password": "Password", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, + ], + 400, + ), + ( + {"password": "aEArV$5Vkdw", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, + ], + 201, + ), + # Test if password is only numeric. + ( + {"password": "1234567890", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, + ], + 400, + ), + ( + {"password": "abc4567890", "username": "TestUser1", "is_superuser": False}, # NOSONAR + [ + {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, + ], + 201, + ), + ], +) +# Disable local password checks to ensure that any ValidationError originates from the Django validators. +@override_settings( + LOCAL_PASSWORD_MIN_LENGTH=1, + LOCAL_PASSWORD_MIN_DIGITS=0, + LOCAL_PASSWORD_MIN_UPPER=0, + LOCAL_PASSWORD_MIN_SPECIAL=0, +) +@pytest.mark.django_db +def test_user_create_with_django_password_validation_ext(post, delete, admin, user_attrs, validators, expected_status_code): + """Test the functionality of the single Django password validators.""" + # + default_parameters = { + # Default values for input parameters which are None. + "user_attrs": { + "password": "r$TyKiOCb#ED", # NOSONAR + "username": "DefaultUser", + "is_superuser": False, + }, + "validators": [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + 'OPTIONS': { + 'min_length': 8, + }, + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, + ], + } + user_attrs = user_attrs if user_attrs is not None else default_parameters["user_attrs"] + validators = validators if validators is not None else default_parameters["validators"] + with override_settings(AUTH_PASSWORD_VALIDATORS=validators): + response = post(reverse('api:user_list'), user_attrs, admin, middleware=SessionMiddleware(mock.Mock())) + assert response.status_code == expected_status_code + # Delete user if it was created succesfully. + if response.status_code == 201: + response = delete(reverse('api:user_detail', kwargs={'pk': response.data['id']}), admin, middleware=SessionMiddleware(mock.Mock())) + assert response.status_code == 204 + else: + # Catch the unexpected behavior that sometimes the user is written + # into the database before the validation fails. This actually can + # happen if UserSerializer.validate instantiates User(**attrs)! + username = user_attrs['username'] + assert not User.objects.filter(username=username) + + @pytest.mark.django_db def test_fail_double_create_user(post, admin): response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock())) @@ -33,6 +201,31 @@ def test_fail_double_create_user(post, admin): assert response.status_code == 400 +@pytest.mark.django_db +def test_creating_user_retains_session(post, admin): + ''' + Creating a new user should not refresh a new session id for the current user. + ''' + with mock.patch('awx.api.serializers.update_session_auth_hash') as update_session_auth_hash: + response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin) + assert response.status_code == 201 + assert not update_session_auth_hash.called + + +@pytest.mark.django_db +def test_updating_own_password_refreshes_session(patch, admin): + ''' + Updating your own password should refresh the session id. + ''' + with mock.patch('awx.api.serializers.update_session_auth_hash') as update_session_auth_hash: + # Attention: If the Django password validator `CommonPasswordValidator` + # is active, this test case will fail because this validator raises on + # password 'newpassword'. Consider changing the hard-coded password to + # something uncommon. + patch(reverse('api:user_detail', kwargs={'pk': admin.pk}), {'password': 'newpassword'}, admin, middleware=SessionMiddleware(mock.Mock())) + assert update_session_auth_hash.called + + @pytest.mark.django_db def test_create_delete_create_user(post, delete, admin): response = post(reverse('api:user_list'), EXAMPLE_USER_DATA, admin, middleware=SessionMiddleware(mock.Mock())) @@ -59,7 +252,23 @@ def test_user_verify_attribute_created(admin, get): resp = get(reverse('api:user_detail', kwargs={'pk': admin.pk}), admin) assert resp.data['created'] == admin.date_joined - past = date(2020, 1, 1).isoformat() + past = "2020-01-01T00:00:00Z" for op, count in (('gt', 1), ('lt', 0)): resp = get(reverse('api:user_list') + f'?created__{op}={past}', admin) assert resp.data['count'] == count + + +@pytest.mark.django_db +def test_org_not_shown_in_admin_user_sublists(admin_user, get, organization): + for view_name in ('user_admin_of_organizations_list', 'user_organizations_list'): + url = reverse(f'api:{view_name}', kwargs={'pk': admin_user.pk}) + r = get(url, user=admin_user, expect=200) + assert organization.pk not in [org['id'] for org in r.data['results']] + + +@pytest.mark.django_db +def test_admin_user_not_shown_in_org_users(admin_user, get, organization): + for view_name in ('organization_users_list', 'organization_admins_list'): + url = reverse(f'api:{view_name}', kwargs={'pk': organization.pk}) + r = get(url, user=admin_user, expect=200) + assert admin_user.pk not in [u['id'] for u in r.data['results']] diff --git a/awx/main/tests/functional/api/test_workflow_job.py b/awx/main/tests/functional/api/test_workflow_job.py new file mode 100644 index 000000000000..36553258dbb4 --- /dev/null +++ b/awx/main/tests/functional/api/test_workflow_job.py @@ -0,0 +1,54 @@ +import pytest + + +from awx.api.versioning import reverse + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "is_admin, status", + [ + [True, 201], + [False, 403], + ], # if they're a WFJ admin, they get a 201 # if they're not a WFJ *nor* org admin, they get a 403 +) +def test_workflow_job_relaunch(workflow_job, post, admin_user, alice, is_admin, status): + url = reverse("api:workflow_job_relaunch", kwargs={'pk': workflow_job.pk}) + if is_admin: + post(url, user=admin_user, expect=status) + else: + post(url, user=alice, expect=status) + + +@pytest.mark.django_db +def test_workflow_job_relaunch_failure(workflow_job, post, admin_user): + workflow_job.is_sliced_job = True + workflow_job.job_template = None + workflow_job.save() + url = reverse("api:workflow_job_relaunch", kwargs={'pk': workflow_job.pk}) + post(url, user=admin_user, expect=400) + + +@pytest.mark.django_db +def test_workflow_job_relaunch_not_inventory_failure(workflow_job, post, admin_user): + workflow_job.is_sliced_job = True + workflow_job.inventory = None + workflow_job.save() + url = reverse("api:workflow_job_relaunch", kwargs={'pk': workflow_job.pk}) + post(url, user=admin_user, expect=400) + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "is_admin, status", + [ + [True, 202], + [False, 403], + ], # if they're a WFJ admin, they get a 202 # if they're not a WFJ *nor* org admin, they get a 403 +) +def test_workflow_job_cancel(workflow_job, post, admin_user, alice, is_admin, status): + url = reverse("api:workflow_job_cancel", kwargs={'pk': workflow_job.pk}) + if is_admin: + post(url, user=admin_user, expect=status) + else: + post(url, user=alice, expect=status) diff --git a/awx/main/tests/functional/commands/test_callback_receiver.py b/awx/main/tests/functional/commands/test_callback_receiver.py index 7b9346fe73e3..f585864cb2a8 100644 --- a/awx/main/tests/functional/commands/test_callback_receiver.py +++ b/awx/main/tests/functional/commands/test_callback_receiver.py @@ -34,48 +34,26 @@ def test_wrapup_does_send_notifications(mocker): mock.assert_called_once_with('succeeded') -class FakeRedis: - def keys(self, *args, **kwargs): - return [] - - def set(self): - pass - - def get(self): - return None - - @classmethod - def from_url(cls, *args, **kwargs): - return cls() - - def pipeline(self): - return self - - class TestCallbackBrokerWorker(TransactionTestCase): @pytest.fixture(autouse=True) - def turn_off_websockets(self): + def turn_off_websockets_and_redis(self, fake_redis): with mock.patch('awx.main.dispatch.worker.callback.emit_event_detail', lambda *a, **kw: None): yield - def get_worker(self): - with mock.patch('redis.Redis', new=FakeRedis): # turn off redis stuff - return CallbackBrokerWorker() - def event_create_kwargs(self): inventory_update = InventoryUpdate.objects.create(source='file', inventory_source=InventorySource.objects.create(source='file')) return dict(inventory_update=inventory_update, created=inventory_update.created) def test_flush_with_valid_event(self): - worker = self.get_worker() + worker = CallbackBrokerWorker() events = [InventoryUpdateEvent(uuid=str(uuid4()), **self.event_create_kwargs())] worker.buff = {InventoryUpdateEvent: events} - worker.flush() + worker.flush(force=True) assert worker.buff.get(InventoryUpdateEvent, []) == [] assert InventoryUpdateEvent.objects.filter(uuid=events[0].uuid).count() == 1 def test_flush_with_invalid_event(self): - worker = self.get_worker() + worker = CallbackBrokerWorker() kwargs = self.event_create_kwargs() events = [ InventoryUpdateEvent(uuid=str(uuid4()), stdout='good1', **kwargs), @@ -83,17 +61,17 @@ def test_flush_with_invalid_event(self): InventoryUpdateEvent(uuid=str(uuid4()), stdout='good2', **kwargs), ] worker.buff = {InventoryUpdateEvent: events.copy()} - worker.flush() + worker.flush(force=True) assert InventoryUpdateEvent.objects.filter(uuid=events[0].uuid).count() == 1 assert InventoryUpdateEvent.objects.filter(uuid=events[1].uuid).count() == 0 assert InventoryUpdateEvent.objects.filter(uuid=events[2].uuid).count() == 1 assert worker.buff == {InventoryUpdateEvent: [events[1]]} def test_duplicate_key_not_saved_twice(self): - worker = self.get_worker() + worker = CallbackBrokerWorker() events = [InventoryUpdateEvent(uuid=str(uuid4()), **self.event_create_kwargs())] worker.buff = {InventoryUpdateEvent: events.copy()} - worker.flush() + worker.flush(force=True) # put current saved event in buffer (error case) worker.buff = {InventoryUpdateEvent: [InventoryUpdateEvent.objects.get(uuid=events[0].uuid)]} @@ -104,7 +82,7 @@ def test_duplicate_key_not_saved_twice(self): assert worker.buff.get(InventoryUpdateEvent, []) == [] def test_give_up_on_bad_event(self): - worker = self.get_worker() + worker = CallbackBrokerWorker() events = [InventoryUpdateEvent(uuid=str(uuid4()), counter=-2, **self.event_create_kwargs())] worker.buff = {InventoryUpdateEvent: events.copy()} @@ -117,7 +95,7 @@ def test_give_up_on_bad_event(self): assert InventoryUpdateEvent.objects.filter(uuid=events[0].uuid).count() == 0 # sanity def test_flush_with_empty_buffer(self): - worker = self.get_worker() + worker = CallbackBrokerWorker() worker.buff = {InventoryUpdateEvent: []} with mock.patch.object(InventoryUpdateEvent.objects, 'bulk_create') as flush_mock: worker.flush() @@ -127,7 +105,7 @@ def test_postgres_invalid_NUL_char(self): # In postgres, text fields reject NUL character, 0x00 # tests use sqlite3 which will not raise an error # but we can still test that it is sanitized before saving - worker = self.get_worker() + worker = CallbackBrokerWorker() kwargs = self.event_create_kwargs() events = [InventoryUpdateEvent(uuid=str(uuid4()), stdout="\x00", **kwargs)] assert "\x00" in events[0].stdout # sanity @@ -135,7 +113,7 @@ def test_postgres_invalid_NUL_char(self): with mock.patch.object(InventoryUpdateEvent.objects, 'bulk_create', side_effect=ValueError): with mock.patch.object(events[0], 'save', side_effect=ValueError): - worker.flush() + worker.flush(force=True) assert "\x00" not in events[0].stdout diff --git a/awx/main/tests/functional/commands/test_cleanup_host_metrics.py b/awx/main/tests/functional/commands/test_cleanup_host_metrics.py new file mode 100644 index 000000000000..ac6d0bde3243 --- /dev/null +++ b/awx/main/tests/functional/commands/test_cleanup_host_metrics.py @@ -0,0 +1,78 @@ +import pytest + +from awx.main.tasks.host_metrics import HostMetricTask +from awx.main.models.inventory import HostMetric +from awx.main.tests.factories.fixtures import mk_host_metric +from dateutil.relativedelta import relativedelta +from django.conf import settings +from django.utils import timezone + + +@pytest.mark.django_db +def test_no_host_metrics(): + """No-crash test""" + assert HostMetric.objects.count() == 0 + HostMetricTask().cleanup(soft_threshold=0, hard_threshold=0) + HostMetricTask().cleanup(soft_threshold=24, hard_threshold=42) + assert HostMetric.objects.count() == 0 + + +@pytest.mark.django_db +def test_delete_exception(): + """Crash test""" + with pytest.raises(ValueError): + HostMetricTask().soft_cleanup("") + with pytest.raises(TypeError): + HostMetricTask().hard_cleanup(set()) + + +@pytest.mark.django_db +@pytest.mark.parametrize('threshold', [settings.CLEANUP_HOST_METRICS_SOFT_THRESHOLD, 20]) +def test_soft_delete(threshold): + """Metrics with last_automation < threshold are updated to deleted=True""" + mk_host_metric('host_1', first_automation=ago(months=1), last_automation=ago(months=1), deleted=False) + mk_host_metric('host_2', first_automation=ago(months=1), last_automation=ago(months=1), deleted=True) + mk_host_metric('host_3', first_automation=ago(months=1), last_automation=ago(months=threshold, hours=-1), deleted=False) + mk_host_metric('host_4', first_automation=ago(months=1), last_automation=ago(months=threshold, hours=-1), deleted=True) + mk_host_metric('host_5', first_automation=ago(months=1), last_automation=ago(months=threshold, hours=1), deleted=False) + mk_host_metric('host_6', first_automation=ago(months=1), last_automation=ago(months=threshold, hours=1), deleted=True) + mk_host_metric('host_7', first_automation=ago(months=1), last_automation=ago(months=42), deleted=False) + mk_host_metric('host_8', first_automation=ago(months=1), last_automation=ago(months=42), deleted=True) + + assert HostMetric.objects.count() == 8 + assert HostMetric.active_objects.count() == 4 + + for i in range(2): + HostMetricTask().cleanup(soft_threshold=threshold) + assert HostMetric.objects.count() == 8 + + hostnames = set(HostMetric.objects.filter(deleted=False).order_by('hostname').values_list('hostname', flat=True)) + assert hostnames == {'host_1', 'host_3'} + + +@pytest.mark.django_db +@pytest.mark.parametrize('threshold', [settings.CLEANUP_HOST_METRICS_HARD_THRESHOLD, 20]) +def test_hard_delete(threshold): + """Metrics with last_deleted < threshold and deleted=True are deleted from the db""" + mk_host_metric('host_1', first_automation=ago(months=1), last_deleted=ago(months=1), deleted=False) + mk_host_metric('host_2', first_automation=ago(months=1), last_deleted=ago(months=1), deleted=True) + mk_host_metric('host_3', first_automation=ago(months=1), last_deleted=ago(months=threshold, hours=-1), deleted=False) + mk_host_metric('host_4', first_automation=ago(months=1), last_deleted=ago(months=threshold, hours=-1), deleted=True) + mk_host_metric('host_5', first_automation=ago(months=1), last_deleted=ago(months=threshold, hours=1), deleted=False) + mk_host_metric('host_6', first_automation=ago(months=1), last_deleted=ago(months=threshold, hours=1), deleted=True) + mk_host_metric('host_7', first_automation=ago(months=1), last_deleted=ago(months=42), deleted=False) + mk_host_metric('host_8', first_automation=ago(months=1), last_deleted=ago(months=42), deleted=True) + + assert HostMetric.objects.count() == 8 + assert HostMetric.active_objects.count() == 4 + + for i in range(2): + HostMetricTask().cleanup(hard_threshold=threshold) + assert HostMetric.objects.count() == 6 + + hostnames = set(HostMetric.objects.order_by('hostname').values_list('hostname', flat=True)) + assert hostnames == {'host_1', 'host_2', 'host_3', 'host_4', 'host_5', 'host_7'} + + +def ago(months=0, hours=0): + return timezone.now() - relativedelta(months=months, hours=hours) diff --git a/awx/main/tests/functional/commands/test_commands.py b/awx/main/tests/functional/commands/test_commands.py index 69f584c2871b..f62dac02064d 100644 --- a/awx/main/tests/functional/commands/test_commands.py +++ b/awx/main/tests/functional/commands/test_commands.py @@ -43,9 +43,9 @@ def run_command(name, *args, **options): ], ) def test_update_password_command(mocker, username, password, expected, changed): - with mocker.patch.object(UpdatePassword, 'update_password', return_value=changed): - result, stdout, stderr = run_command('update_password', username=username, password=password) - if result is None: - assert stdout == expected - else: - assert str(result) == expected + mocker.patch.object(UpdatePassword, 'update_password', return_value=changed) + result, stdout, stderr = run_command('update_password', username=username, password=password) + if result is None: + assert stdout == expected + else: + assert str(result) == expected diff --git a/awx/main/tests/functional/commands/test_host_metric_summary_monthly.py b/awx/main/tests/functional/commands/test_host_metric_summary_monthly.py new file mode 100644 index 000000000000..525fdd789ff0 --- /dev/null +++ b/awx/main/tests/functional/commands/test_host_metric_summary_monthly.py @@ -0,0 +1,382 @@ +import pytest +import datetime +from dateutil.relativedelta import relativedelta +from django.conf import settings +from django.utils import timezone + + +from awx.main.management.commands.host_metric_summary_monthly import Command +from awx.main.models.inventory import HostMetric, HostMetricSummaryMonthly +from awx.main.tests.factories.fixtures import mk_host_metric, mk_host_metric_summary + + +@pytest.fixture +def threshold(): + return int(getattr(settings, 'CLEANUP_HOST_METRICS_HARD_THRESHOLD', 36)) + + +@pytest.mark.django_db +@pytest.mark.parametrize("metrics_cnt", [0, 1, 2, 3]) +@pytest.mark.parametrize("mode", ["old_data", "actual_data", "all_data"]) +def test_summaries_counts(threshold, metrics_cnt, mode): + assert HostMetricSummaryMonthly.objects.count() == 0 + + for idx in range(metrics_cnt): + if mode == "old_data" or mode == "all_data": + mk_host_metric(None, months_ago(threshold + idx, "dt")) + elif mode == "actual_data" or mode == "all_data": + mk_host_metric(None, (months_ago(threshold - idx, "dt"))) + + Command().handle() + + # Number of records is equal to host metrics' hard cleanup months + assert HostMetricSummaryMonthly.objects.count() == threshold + + # Records start with date in the month following to the threshold month + date = months_ago(threshold - 1) + for metric in list(HostMetricSummaryMonthly.objects.order_by('date').all()): + assert metric.date == date + date += relativedelta(months=1) + + # Older record are untouched + mk_host_metric_summary(date=months_ago(threshold + 10)) + Command().handle() + + assert HostMetricSummaryMonthly.objects.count() == threshold + 1 + + +@pytest.mark.django_db +@pytest.mark.parametrize("mode", ["old_data", "actual_data", "all_data"]) +def test_summary_values(threshold, mode): + tester = {"old_data": MetricsTesterOldData(threshold), "actual_data": MetricsTesterActualData(threshold), "all_data": MetricsTesterCombinedData(threshold)}[ + mode + ] + + for iteration in ["create_metrics", "add_old_summaries", "change_metrics", "delete_metrics", "add_metrics"]: + getattr(tester, iteration)() # call method by string + + # Operation is idempotent, repeat twice + for _ in range(2): + Command().handle() + # call assert method by string + getattr(tester, f"assert_{iteration}")() + + +class MetricsTester: + def __init__(self, threshold, ignore_asserts=False): + self.threshold = threshold + self.expected_summaries = {} + self.ignore_asserts = ignore_asserts + + def add_old_summaries(self): + """These records don't correspond with Host metrics""" + mk_host_metric_summary(self.below(4), license_consumed=100, hosts_added=10, hosts_deleted=5) + mk_host_metric_summary(self.below(3), license_consumed=105, hosts_added=20, hosts_deleted=10) + mk_host_metric_summary(self.below(2), license_consumed=115, hosts_added=60, hosts_deleted=75) + + def assert_add_old_summaries(self): + """Old summary records should be untouched""" + self.expected_summaries[self.below(4)] = {"date": self.below(4), "license_consumed": 100, "hosts_added": 10, "hosts_deleted": 5} + self.expected_summaries[self.below(3)] = {"date": self.below(3), "license_consumed": 105, "hosts_added": 20, "hosts_deleted": 10} + self.expected_summaries[self.below(2)] = {"date": self.below(2), "license_consumed": 115, "hosts_added": 60, "hosts_deleted": 75} + + self.assert_host_metric_summaries() + + def assert_host_metric_summaries(self): + """Ignore asserts when old/actual test object is used only as a helper for Combined test""" + if self.ignore_asserts: + return True + + for summary in list(HostMetricSummaryMonthly.objects.order_by('date').all()): + assert self.expected_summaries.get(summary.date, None) is not None + + assert self.expected_summaries[summary.date] == { + "date": summary.date, + "license_consumed": summary.license_consumed, + "hosts_added": summary.hosts_added, + "hosts_deleted": summary.hosts_deleted, + } + + def below(self, months, fmt="date"): + """months below threshold, returns first date of that month""" + date = months_ago(self.threshold + months) + if fmt == "dt": + return timezone.make_aware(datetime.datetime.combine(date, datetime.datetime.min.time())) + else: + return date + + def above(self, months, fmt="date"): + """months above threshold, returns first date of that month""" + date = months_ago(self.threshold - months) + if fmt == "dt": + return timezone.make_aware(datetime.datetime.combine(date, datetime.datetime.min.time())) + else: + return date + + +class MetricsTesterOldData(MetricsTester): + def create_metrics(self): + """Creates 7 host metrics older than delete threshold""" + mk_host_metric("host_1", first_automation=self.below(3, "dt")) + mk_host_metric("host_2", first_automation=self.below(2, "dt")) + mk_host_metric("host_3", first_automation=self.below(2, "dt"), last_deleted=self.above(2, "dt"), deleted=False) + mk_host_metric("host_4", first_automation=self.below(2, "dt"), last_deleted=self.above(2, "dt"), deleted=True) + mk_host_metric("host_5", first_automation=self.below(2, "dt"), last_deleted=self.below(2, "dt"), deleted=True) + mk_host_metric("host_6", first_automation=self.below(1, "dt"), last_deleted=self.below(1, "dt"), deleted=False) + mk_host_metric("host_7", first_automation=self.below(1, "dt")) + + def assert_create_metrics(self): + """ + Month 1 is computed from older host metrics, + Month 2 has deletion (host_4) + Other months are unchanged (same as month 2) + """ + self.expected_summaries = { + self.above(1): {"date": self.above(1), "license_consumed": 6, "hosts_added": 0, "hosts_deleted": 0}, + self.above(2): {"date": self.above(2), "license_consumed": 5, "hosts_added": 0, "hosts_deleted": 1}, + } + # no change in months 3+ + idx = 3 + month = self.above(idx) + while month <= beginning_of_the_month(): + self.expected_summaries[self.above(idx)] = {"date": self.above(idx), "license_consumed": 5, "hosts_added": 0, "hosts_deleted": 0} + month += relativedelta(months=1) + idx += 1 + + self.assert_host_metric_summaries() + + def add_old_summaries(self): + super().add_old_summaries() + + def assert_add_old_summaries(self): + super().assert_add_old_summaries() + + @staticmethod + def change_metrics(): + """Hosts 1,2 soft deleted, host_4 automated again (undeleted)""" + HostMetric.objects.filter(hostname='host_1').update(last_deleted=beginning_of_the_month("dt"), deleted=True) + HostMetric.objects.filter(hostname='host_2').update(last_deleted=timezone.now(), deleted=True) + HostMetric.objects.filter(hostname='host_4').update(deleted=False) + + def assert_change_metrics(self): + """ + Summaries since month 2 were changed (host_4 restored == automated again) + Current month has 2 deletions (host_1, host_2) + """ + self.expected_summaries[self.above(2)] |= {'hosts_deleted': 0} + for idx in range(2, self.threshold): + self.expected_summaries[self.above(idx)] |= {'license_consumed': 6} + self.expected_summaries[beginning_of_the_month()] |= {'license_consumed': 4, 'hosts_deleted': 2} + + self.assert_host_metric_summaries() + + @staticmethod + def delete_metrics(): + """Deletes metric deleted before the threshold""" + HostMetric.objects.filter(hostname='host_5').delete() + + def assert_delete_metrics(self): + """No change""" + self.assert_host_metric_summaries() + + @staticmethod + def add_metrics(): + """Adds new metrics""" + mk_host_metric("host_24", first_automation=beginning_of_the_month("dt")) + mk_host_metric("host_25", first_automation=beginning_of_the_month("dt")) # timezone.now()) + + def assert_add_metrics(self): + """Summary in current month is updated""" + self.expected_summaries[beginning_of_the_month()]['license_consumed'] = 6 + self.expected_summaries[beginning_of_the_month()]['hosts_added'] = 2 + + self.assert_host_metric_summaries() + + +class MetricsTesterActualData(MetricsTester): + def create_metrics(self): + """Creates 16 host metrics newer than delete threshold""" + mk_host_metric("host_8", first_automation=self.above(1, "dt")) + mk_host_metric("host_9", first_automation=self.above(1, "dt"), last_deleted=self.above(1, "dt")) + mk_host_metric("host_10", first_automation=self.above(1, "dt"), last_deleted=self.above(1, "dt"), deleted=True) + mk_host_metric("host_11", first_automation=self.above(1, "dt"), last_deleted=self.above(2, "dt")) + mk_host_metric("host_12", first_automation=self.above(1, "dt"), last_deleted=self.above(2, "dt"), deleted=True) + mk_host_metric("host_13", first_automation=self.above(2, "dt")) + mk_host_metric("host_14", first_automation=self.above(2, "dt"), last_deleted=self.above(2, "dt")) + mk_host_metric("host_15", first_automation=self.above(2, "dt"), last_deleted=self.above(2, "dt"), deleted=True) + mk_host_metric("host_16", first_automation=self.above(2, "dt"), last_deleted=self.above(3, "dt")) + mk_host_metric("host_17", first_automation=self.above(2, "dt"), last_deleted=self.above(3, "dt"), deleted=True) + mk_host_metric("host_18", first_automation=self.above(4, "dt")) + # next one shouldn't happen in real (deleted=True, last_deleted = NULL) + mk_host_metric("host_19", first_automation=self.above(4, "dt"), deleted=True) + mk_host_metric("host_20", first_automation=self.above(4, "dt"), last_deleted=self.above(4, "dt")) + mk_host_metric("host_21", first_automation=self.above(4, "dt"), last_deleted=self.above(4, "dt"), deleted=True) + mk_host_metric("host_22", first_automation=self.above(4, "dt"), last_deleted=self.above(5, "dt")) + mk_host_metric("host_23", first_automation=self.above(4, "dt"), last_deleted=self.above(5, "dt"), deleted=True) + + def assert_create_metrics(self): + self.expected_summaries = { + self.above(1): {"date": self.above(1), "license_consumed": 4, "hosts_added": 5, "hosts_deleted": 1}, + self.above(2): {"date": self.above(2), "license_consumed": 7, "hosts_added": 5, "hosts_deleted": 2}, + self.above(3): {"date": self.above(3), "license_consumed": 6, "hosts_added": 0, "hosts_deleted": 1}, + self.above(4): {"date": self.above(4), "license_consumed": 11, "hosts_added": 6, "hosts_deleted": 1}, + self.above(5): {"date": self.above(5), "license_consumed": 10, "hosts_added": 0, "hosts_deleted": 1}, + } + # no change in months 6+ + idx = 6 + month = self.above(idx) + while month <= beginning_of_the_month(): + self.expected_summaries[self.above(idx)] = {"date": self.above(idx), "license_consumed": 10, "hosts_added": 0, "hosts_deleted": 0} + month += relativedelta(months=1) + idx += 1 + + self.assert_host_metric_summaries() + + def add_old_summaries(self): + super().add_old_summaries() + + def assert_add_old_summaries(self): + super().assert_add_old_summaries() + + @staticmethod + def change_metrics(): + """ + - Hosts 12, 19, 21 were automated again (undeleted) + - Host 16 was soft deleted + - Host 17 was undeleted and soft deleted again + """ + HostMetric.objects.filter(hostname='host_12').update(deleted=False) + HostMetric.objects.filter(hostname='host_16').update(last_deleted=timezone.now(), deleted=True) + HostMetric.objects.filter(hostname='host_17').update(last_deleted=beginning_of_the_month("dt"), deleted=True) + HostMetric.objects.filter(hostname='host_19').update(deleted=False) + HostMetric.objects.filter(hostname='host_21').update(deleted=False) + + def assert_change_metrics(self): + """ + Summaries since month 2 were changed + Current month has 2 deletions (host_16, host_17) + """ + self.expected_summaries[self.above(2)] |= {'license_consumed': 8, 'hosts_deleted': 1} + self.expected_summaries[self.above(3)] |= {'license_consumed': 8, 'hosts_deleted': 0} + self.expected_summaries[self.above(4)] |= {'license_consumed': 14, 'hosts_deleted': 0} + + # month 5 had hosts_deleted 1 => license_consumed == 14 - 1 + for idx in range(5, self.threshold): + self.expected_summaries[self.above(idx)] |= {'license_consumed': 13} + self.expected_summaries[beginning_of_the_month()] |= {'license_consumed': 11, 'hosts_deleted': 2} + + self.assert_host_metric_summaries() + + def delete_metrics(self): + """Hard cleanup can't delete metrics newer than threshold. No change""" + pass + + def assert_delete_metrics(self): + """No change""" + self.assert_host_metric_summaries() + + @staticmethod + def add_metrics(): + """Adds new metrics""" + mk_host_metric("host_26", first_automation=beginning_of_the_month("dt")) + mk_host_metric("host_27", first_automation=timezone.now()) + + def assert_add_metrics(self): + """ + Two metrics were deleted in current month by change_metrics() + Two metrics are added now + => license_consumed is equal to the previous month (13 - 2 + 2) + """ + self.expected_summaries[beginning_of_the_month()] |= {'license_consumed': 13, 'hosts_added': 2} + + self.assert_host_metric_summaries() + + +class MetricsTesterCombinedData(MetricsTester): + def __init__(self, threshold): + super().__init__(threshold) + self.old_data = MetricsTesterOldData(threshold, ignore_asserts=True) + self.actual_data = MetricsTesterActualData(threshold, ignore_asserts=True) + + def assert_host_metric_summaries(self): + self._combine_expected_summaries() + super().assert_host_metric_summaries() + + def create_metrics(self): + self.old_data.create_metrics() + self.actual_data.create_metrics() + + def assert_create_metrics(self): + self.old_data.assert_create_metrics() + self.actual_data.assert_create_metrics() + + self.assert_host_metric_summaries() + + def add_old_summaries(self): + super().add_old_summaries() + + def assert_add_old_summaries(self): + self.old_data.assert_add_old_summaries() + self.actual_data.assert_add_old_summaries() + + self.assert_host_metric_summaries() + + def change_metrics(self): + self.old_data.change_metrics() + self.actual_data.change_metrics() + + def assert_change_metrics(self): + self.old_data.assert_change_metrics() + self.actual_data.assert_change_metrics() + + self.assert_host_metric_summaries() + + def delete_metrics(self): + self.old_data.delete_metrics() + self.actual_data.delete_metrics() + + def assert_delete_metrics(self): + self.old_data.assert_delete_metrics() + self.actual_data.assert_delete_metrics() + + self.assert_host_metric_summaries() + + def add_metrics(self): + self.old_data.add_metrics() + self.actual_data.add_metrics() + + def assert_add_metrics(self): + self.old_data.assert_add_metrics() + self.actual_data.assert_add_metrics() + + self.assert_host_metric_summaries() + + def _combine_expected_summaries(self): + """ + Expected summaries are sum of expected values for tests with old and actual data + Except data older than hard delete threshold (these summaries are untouched by task => the same in all tests) + """ + for date, summary in self.old_data.expected_summaries.items(): + if date <= months_ago(self.threshold): + license_consumed = summary['license_consumed'] + hosts_added = summary['hosts_added'] + hosts_deleted = summary['hosts_deleted'] + else: + license_consumed = summary['license_consumed'] + self.actual_data.expected_summaries[date]['license_consumed'] + hosts_added = summary['hosts_added'] + self.actual_data.expected_summaries[date]['hosts_added'] + hosts_deleted = summary['hosts_deleted'] + self.actual_data.expected_summaries[date]['hosts_deleted'] + self.expected_summaries[date] = {'date': date, 'license_consumed': license_consumed, 'hosts_added': hosts_added, 'hosts_deleted': hosts_deleted} + + +def months_ago(num, fmt="date"): + if num is None: + return None + return beginning_of_the_month(fmt) - relativedelta(months=num) + + +def beginning_of_the_month(fmt="date"): + date = datetime.date.today().replace(day=1) + if fmt == "dt": + return timezone.make_aware(datetime.datetime.combine(date, datetime.datetime.min.time())) + else: + return date diff --git a/awx/main/tests/functional/commands/test_inventory_import.py b/awx/main/tests/functional/commands/test_inventory_import.py index 75a09fc47627..d560b1fa70de 100644 --- a/awx/main/tests/functional/commands/test_inventory_import.py +++ b/awx/main/tests/functional/commands/test_inventory_import.py @@ -18,7 +18,6 @@ from awx.main.models import Inventory, Host, Group, InventorySource from awx.main.utils.mem_inventory import MemGroup - TEST_INVENTORY_CONTENT = { "_meta": {"hostvars": {}}, "all": {"children": ["others", "servers", "ungrouped"], "vars": {"vara": "A"}}, @@ -306,6 +305,47 @@ def test_memberships_are_respected(self, inventory): has_host_group = inventory.groups.get(name='has_a_host') assert has_host_group.hosts.count() == 1 + @mock.patch.object(inventory_import, 'AnsibleInventoryLoader', MockLoader) + def test_overwrite_removes_stale_memberships(self, inventory): + """When overwrite is enabled, host-group and group-group memberships + that are no longer in the imported data should be removed.""" + # First import: parent_group has two children, host_group has two hosts + inventory_import.AnsibleInventoryLoader._data = { + "_meta": {"hostvars": {"host1": {}, "host2": {}}}, + "all": {"children": ["ungrouped", "parent_group", "child_a", "child_b", "host_group"]}, + "parent_group": {"children": ["child_a", "child_b"]}, + "host_group": {"hosts": ["host1", "host2"]}, + "ungrouped": {"hosts": []}, + } + cmd = inventory_import.Command() + cmd.handle(inventory_id=inventory.pk, source=__file__, overwrite=True) + + parent = inventory.groups.get(name='parent_group') + assert set(parent.children.values_list('name', flat=True)) == {'child_a', 'child_b'} + host_grp = inventory.groups.get(name='host_group') + assert set(host_grp.hosts.values_list('name', flat=True)) == {'host1', 'host2'} + + # Second import: child_b removed from parent_group, host2 moved out of host_group + inventory_import.AnsibleInventoryLoader._data = { + "_meta": {"hostvars": {"host1": {}, "host2": {}}}, + "all": {"children": ["ungrouped", "parent_group", "child_a", "child_b", "host_group"]}, + "parent_group": {"children": ["child_a"]}, + "host_group": {"hosts": ["host1"]}, + "ungrouped": {"hosts": ["host2"]}, + } + cmd = inventory_import.Command() + cmd.handle(inventory_id=inventory.pk, source=__file__, overwrite=True) + + parent.refresh_from_db() + host_grp.refresh_from_db() + # child_b should be removed from parent_group + assert set(parent.children.values_list('name', flat=True)) == {'child_a'} + # host2 should be removed from host_group + assert set(host_grp.hosts.values_list('name', flat=True)) == {'host1'} + # host2 and child_b should still exist in the inventory, just not in those groups + assert inventory.hosts.filter(name='host2').exists() + assert inventory.groups.filter(name='child_b').exists() + @mock.patch.object(inventory_import, 'AnsibleInventoryLoader', MockLoader) def test_recursive_group_error(self, inventory): inventory_import.AnsibleInventoryLoader._data = { diff --git a/awx/main/tests/functional/commands/test_oauth2_token_create.py b/awx/main/tests/functional/commands/test_oauth2_token_create.py deleted file mode 100644 index 5c7a13813717..000000000000 --- a/awx/main/tests/functional/commands/test_oauth2_token_create.py +++ /dev/null @@ -1,44 +0,0 @@ -# Python -import pytest -import string -import random -from io import StringIO - -# Django -from django.contrib.auth.models import User -from django.core.management import call_command -from django.core.management.base import CommandError - -# AWX -from awx.main.models.oauth import OAuth2AccessToken - - -@pytest.mark.django_db -@pytest.mark.inventory_import -class TestOAuth2CreateCommand: - def test_no_user_option(self): - out = StringIO() - with pytest.raises(CommandError) as excinfo: - call_command('create_oauth2_token', stdout=out) - assert 'Username not supplied.' in str(excinfo.value) - out.close() - - def test_non_existing_user(self): - out = StringIO() - fake_username = '' - while fake_username == '' or User.objects.filter(username=fake_username).exists(): - fake_username = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) - arg = '--user=' + fake_username - with pytest.raises(CommandError) as excinfo: - call_command('create_oauth2_token', arg, stdout=out) - assert 'The user does not exist.' in str(excinfo.value) - out.close() - - def test_correct_user(self, alice): - out = StringIO() - arg = '--user=' + 'alice' - call_command('create_oauth2_token', arg, stdout=out) - generated_token = out.getvalue().strip() - assert OAuth2AccessToken.objects.filter(user=alice, token=generated_token).count() == 1 - assert OAuth2AccessToken.objects.get(user=alice, token=generated_token).scope == 'write' - out.close() diff --git a/awx/main/tests/functional/commands/test_oauth2_token_revoke.py b/awx/main/tests/functional/commands/test_oauth2_token_revoke.py deleted file mode 100644 index 69b25fd0a849..000000000000 --- a/awx/main/tests/functional/commands/test_oauth2_token_revoke.py +++ /dev/null @@ -1,62 +0,0 @@ -# Python -import datetime -import pytest -import string -import random -from io import StringIO - -# Django -from django.core.management import call_command -from django.core.management.base import CommandError - -# AWX -from awx.main.models import RefreshToken -from awx.main.models.oauth import OAuth2AccessToken -from awx.api.versioning import reverse - - -@pytest.mark.django_db -class TestOAuth2RevokeCommand: - def test_non_existing_user(self): - out = StringIO() - fake_username = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) - arg = '--user=' + fake_username - with pytest.raises(CommandError) as excinfo: - call_command('revoke_oauth2_tokens', arg, stdout=out) - assert 'A user with that username does not exist' in str(excinfo.value) - out.close() - - def test_revoke_all_access_tokens(self, post, admin, alice): - url = reverse('api:o_auth2_token_list') - for user in (admin, alice): - post(url, {'description': 'test token', 'scope': 'read'}, user) - assert OAuth2AccessToken.objects.count() == 2 - call_command('revoke_oauth2_tokens') - assert OAuth2AccessToken.objects.count() == 0 - - def test_revoke_access_token_for_user(self, post, admin, alice): - url = reverse('api:o_auth2_token_list') - post(url, {'description': 'test token', 'scope': 'read'}, alice) - assert OAuth2AccessToken.objects.count() == 1 - call_command('revoke_oauth2_tokens', '--user=admin') - assert OAuth2AccessToken.objects.count() == 1 - call_command('revoke_oauth2_tokens', '--user=alice') - assert OAuth2AccessToken.objects.count() == 0 - - def test_revoke_all_refresh_tokens(self, post, admin, oauth_application): - url = reverse('api:o_auth2_token_list') - post(url, {'description': 'test token for', 'scope': 'read', 'application': oauth_application.pk}, admin) - assert OAuth2AccessToken.objects.count() == 1 - assert RefreshToken.objects.count() == 1 - - call_command('revoke_oauth2_tokens') - assert OAuth2AccessToken.objects.count() == 0 - assert RefreshToken.objects.count() == 1 - for r in RefreshToken.objects.all(): - assert r.revoked is None - - call_command('revoke_oauth2_tokens', '--all') - assert RefreshToken.objects.count() == 1 - for r in RefreshToken.objects.all(): - assert r.revoked is not None - assert isinstance(r.revoked, datetime.datetime) diff --git a/awx/main/tests/functional/commands/test_secret_key_regeneration.py b/awx/main/tests/functional/commands/test_secret_key_regeneration.py index 808fefbdc997..05584e5101e7 100644 --- a/awx/main/tests/functional/commands/test_secret_key_regeneration.py +++ b/awx/main/tests/functional/commands/test_secret_key_regeneration.py @@ -12,7 +12,6 @@ from awx.main.management.commands import regenerate_secret_key from awx.main.utils.encryption import encrypt_field, decrypt_field, encrypt_value - PREFIX = '$encrypted$UTF8$AESCBC$' @@ -147,22 +146,6 @@ def test_survey_spec(self, inventory, project, survey_spec_factory, cls): with override_settings(SECRET_KEY=new_key): assert json.loads(new_job.decrypted_extra_vars())['secret_key'] == 'donttell' - def test_oauth2_application_client_secret(self, oauth_application): - # test basic decryption - secret = oauth_application.client_secret - assert len(secret) == 128 - - # re-key the client_secret - new_key = regenerate_secret_key.Command().handle() - - # verify that the old SECRET_KEY doesn't work - with pytest.raises(InvalidToken): - models.OAuth2Application.objects.get(pk=oauth_application.pk).client_secret - - # verify that the new SECRET_KEY *does* work - with override_settings(SECRET_KEY=new_key): - assert models.OAuth2Application.objects.get(pk=oauth_application.pk).client_secret == secret - def test_use_custom_key_with_tower_secret_key_env_var(self): custom_key = 'MXSq9uqcwezBOChl/UfmbW1k4op+bC+FQtwPqgJ1u9XV' os.environ['TOWER_SECRET_KEY'] = custom_key diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index 4f8b6bc83c5a..8f2186112090 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -1,22 +1,28 @@ +import logging + # Python import pytest from unittest import mock -import tempfile -import shutil import urllib.parse from unittest.mock import PropertyMock +import importlib # Django from django.urls import resolve from django.http import Http404 +from django.apps import apps as global_apps from django.core.handlers.exception import response_for_exception from django.contrib.auth.models import User from django.core.serializers.json import DjangoJSONEncoder from django.db.backends.sqlite3.base import SQLiteCursorWrapper +from django.db.models.signals import post_migrate + +from awx.main.migrations._dab_rbac import setup_managed_role_definitions + # AWX from awx.main.models.projects import Project -from awx.main.models.ha import Instance +from awx.main.models.ha import Instance, InstanceGroup from rest_framework.test import ( APIRequestFactory, @@ -30,7 +36,6 @@ Organization, Team, ) -from awx.main.models.rbac import Role from awx.main.models.notifications import NotificationTemplate, Notification from awx.main.models.events import ( JobEvent, @@ -41,17 +46,82 @@ ) from awx.main.models.workflow import WorkflowJobTemplate from awx.main.models.ad_hoc_commands import AdHocCommand -from awx.main.models.oauth import OAuth2Application as Application from awx.main.models.execution_environments import ExecutionEnvironment +from awx.main.utils import is_testing + +logger = logging.getLogger(__name__) __SWAGGER_REQUESTS__ = {} +# HACK: the dab_resource_registry app required ServiceID in migrations which checks do not run +dab_rr_initial = importlib.import_module('ansible_base.resource_registry.migrations.0001_initial') + + +def create_service_id(app_config, apps=global_apps, **kwargs): + try: + apps.get_model("dab_resource_registry", "ServiceID") + except LookupError: + logger.info('Looks like reverse migration, not creating resource registry ServiceID') + return + dab_rr_initial.create_service_id(apps, None) + + +if is_testing(): + post_migrate.connect(create_service_id) + + @pytest.fixture(scope="session") def swagger_autogen(requests=__SWAGGER_REQUESTS__): return requests +class FakeRedis: + def __init__(self, *args, **kwargs): + # Accept and ignore all arguments to match redis.Redis signature + pass + + def keys(self, *args, **kwargs): + return [] + + def set(self, *args, **kwargs): + pass + + def get(self, *args, **kwargs): + return None + + def rpush(self, *args, **kwargs): + return 1 + + def blpop(self, *args, **kwargs): + return None + + def delete(self, *args, **kwargs): + pass + + def llen(self, *args, **kwargs): + return 0 + + def scan_iter(self, *args, **kwargs): + return iter([]) + + @classmethod + def from_url(cls, *args, **kwargs): + return cls() + + def pipeline(self): + return self + + def ping(self): + return + + +@pytest.fixture +def fake_redis(): + with mock.patch('redis.Redis', new=FakeRedis): # turn off redis stuff + yield + + @pytest.fixture def user(): def u(name, is_superuser=False): @@ -80,6 +150,17 @@ def deploy_jobtemplate(project, inventory, credential): return jt +@pytest.fixture() +def execution_environment(): + return ExecutionEnvironment.objects.create(name="test-ee", description="test-ee", managed=True) + + +@pytest.fixture +def setup_managed_roles(): + "Run the migration script to pre-create managed role definitions" + setup_managed_role_definitions(global_apps, None) + + @pytest.fixture def team(organization): return organization.teams.create(name='test-team') @@ -92,20 +173,6 @@ def team_member(user, team): return ret -@pytest.fixture(scope="session", autouse=True) -def project_playbooks(): - """ - Return playbook_files as playbooks for manual projects when testing. - """ - - class PlaybooksMock(mock.PropertyMock): - def __get__(self, obj, obj_type): - return obj.playbook_files - - mocked = mock.patch.object(Project, 'playbooks', new_callable=PlaybooksMock) - mocked.start() - - @pytest.fixture def run_computed_fields_right_away(request): def run_me(inventory_id): @@ -178,12 +245,6 @@ def factory(name): return factory -@pytest.fixture -def user_project(user): - owner = user('owner') - return Project.objects.create(name="test-user-project", created_by=owner, description="test-user-project-desc") - - @pytest.fixture def insights_project(): return Project.objects.create(name="test-insights-project", scm_type="insights") @@ -333,13 +394,6 @@ def inventory(organization): return organization.inventories.create(name="test-inv") -@pytest.fixture -def insights_inventory(inventory): - inventory.scm_type = 'insights' - inventory.save() - return inventory - - @pytest.fixture def scm_inventory_source(inventory, project): inv_src = InventorySource( @@ -423,7 +477,7 @@ def admin(user): @pytest.fixture def system_auditor(user): u = user('an-auditor', False) - Role.singleton('system_auditor').members.add(u) + u.is_system_auditor = True return u @@ -490,25 +544,16 @@ def g(name): @pytest.fixture -def hosts(group_factory): - group1 = group_factory('group-1') - - def rf(host_count=1): - hosts = [] - for i in range(0, host_count): - name = '%s-host-%s' % (group1.name, i) - (host, created) = group1.inventory.hosts.get_or_create(name=name) - if created: - group1.hosts.add(host) - hosts.append(host) - return hosts - - return rf +def group(inventory): + return inventory.groups.create(name='single-group') @pytest.fixture -def group(inventory): - return inventory.groups.create(name='single-group') +def constructed_inventory(organization): + """ + creates a new constructed inventory source + """ + return Inventory.objects.create(name='dummy1', kind='constructed', organization=organization) @pytest.fixture @@ -704,6 +749,11 @@ def jt_linked(organization, project, inventory, machine_credential, credential, return jt +@pytest.fixture +def instance_group(): + return InstanceGroup.objects.create(name="east") + + @pytest.fixture def workflow_job_template(organization): wjt = WorkflowJobTemplate.objects.create(name='test-workflow_job_template', organization=organization) @@ -735,6 +785,30 @@ def factory(system_job_template=system_job_template, initial_state='new', create return factory +@pytest.fixture +def wfjt(workflow_job_template_factory, organization): + objects = workflow_job_template_factory('test_workflow', organization=organization, persisted=True) + return objects.workflow_job_template + + +@pytest.fixture +def wfjt_with_nodes(workflow_job_template_factory, organization, job_template): + objects = workflow_job_template_factory( + 'test_workflow', organization=organization, workflow_job_template_nodes=[{'unified_job_template': job_template}], persisted=True + ) + return objects.workflow_job_template + + +@pytest.fixture +def wfjt_node(wfjt_with_nodes): + return wfjt_with_nodes.workflow_job_template_nodes.all()[0] + + +@pytest.fixture +def workflow_job(wfjt): + return wfjt.workflow_jobs.create(name='test_workflow') + + def dumps(value): return DjangoJSONEncoder().encode(value) @@ -752,30 +826,43 @@ def get_db_prep_save(self, value, connection, **kwargs): return value -@pytest.fixture -def oauth_application(admin): - return Application.objects.create(name='test app', user=admin, client_type='confidential', authorization_grant_type='password') - - -@pytest.fixture -def sqlite_copy_expert(request): - # copy_expert is postgres-specific, and SQLite doesn't support it; mock its - # behavior to test that it writes a file that contains stdout from events - path = tempfile.mkdtemp(prefix='job-event-stdout') +class MockCopy: + events = [] + index = -1 - def write_stdout(self, sql, fd): - # simulate postgres copy_expert support with ORM code + def __init__(self, sql): + self.events = [] parts = sql.split(' ') tablename = parts[parts.index('from') + 1] for cls in (JobEvent, AdHocCommandEvent, ProjectUpdateEvent, InventoryUpdateEvent, SystemJobEvent): if cls._meta.db_table == tablename: for event in cls.objects.order_by('start_line').all(): - fd.write(event.stdout) + self.events.append(event.stdout) + + def read(self): + self.index = self.index + 1 + if self.index < len(self.events): + return memoryview(self.events[self.index].encode()) + + return None + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +@pytest.fixture +def sqlite_copy(request, mocker): + # copy is postgres-specific, and SQLite doesn't support it; mock its + # behavior to test that it writes a file that contains stdout from events + + def write_stdout(self, sql): + mock_copy = MockCopy(sql) + return mock_copy - setattr(SQLiteCursorWrapper, 'copy_expert', write_stdout) - request.addfinalizer(lambda: shutil.rmtree(path)) - request.addfinalizer(lambda: delattr(SQLiteCursorWrapper, 'copy_expert')) - return path + mocker.patch.object(SQLiteCursorWrapper, 'copy', write_stdout, create=True) @pytest.fixture diff --git a/awx/main/tests/functional/dab_feature_flags/test_feature_flags_api.py b/awx/main/tests/functional/dab_feature_flags/test_feature_flags_api.py new file mode 100644 index 000000000000..fb483000fb90 --- /dev/null +++ b/awx/main/tests/functional/dab_feature_flags/test_feature_flags_api.py @@ -0,0 +1,35 @@ +import pytest +from flags.state import get_flags, flag_state +from ansible_base.feature_flags.models import AAPFlag +from ansible_base.feature_flags.utils import create_initial_data as seed_feature_flags +from django.conf import settings +from awx.main.models import User + + +@pytest.mark.django_db +def test_feature_flags_list_endpoint(get): + bob = User.objects.create(username='bob', password='test_user', is_superuser=True) + url = "/api/v2/feature_flags/states/" + response = get(url, user=bob, expect=200) + assert len(get_flags()) > 0 + assert len(response.data["results"]) == len(get_flags()) + + +@pytest.mark.django_db +@pytest.mark.parametrize('flag_val', (True, False)) +def test_feature_flags_list_endpoint_override(get, flag_val): + bob = User.objects.create(username='bob', password='test_user', is_superuser=True) + + AAPFlag.objects.all().delete() + flag_name = "FEATURE_INDIRECT_NODE_COUNTING_ENABLED" + setattr(settings, flag_name, flag_val) + seed_feature_flags() + url = "/api/v2/feature_flags/states/" + response = get(url, user=bob, expect=200) + + results = response.data["results"] + flag_names = [flag["name"] for flag in results] + + assert flag_name in flag_names, f"{flag_name} should be present in feature flags" + assert all(name.startswith("FEATURE_") for name in flag_names), "All feature flags should start with FEATURE_ prefix" + assert flag_state(flag_name) == flag_val diff --git a/awx/main/tests/functional/dab_rbac/test_access_list.py b/awx/main/tests/functional/dab_rbac/test_access_list.py new file mode 100644 index 000000000000..0a409efa9c8a --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_access_list.py @@ -0,0 +1,125 @@ +import pytest + +from awx.main.models import User +from awx.api.versioning import reverse + + +@pytest.mark.django_db +def test_access_list_superuser(get, admin_user, inventory): + url = reverse('api:inventory_access_list', kwargs={'pk': inventory.id}) + + response = get(url, user=admin_user, expect=200) + by_username = {} + for entry in response.data['results']: + by_username[entry['username']] = entry + assert 'admin' in by_username + + assert len(by_username['admin']['summary_fields']['indirect_access']) == 1 + assert len(by_username['admin']['summary_fields']['direct_access']) == 0 + access_entry = by_username['admin']['summary_fields']['indirect_access'][0] + assert sorted(access_entry['descendant_roles']) == sorted(['adhoc_role', 'use_role', 'update_role', 'read_role', 'admin_role']) + + +@pytest.mark.django_db +def test_access_list_system_auditor(get, admin_user, inventory): + sys_auditor = User.objects.create(username='sys-aud') + sys_auditor.is_system_auditor = True + assert sys_auditor.is_system_auditor + url = reverse('api:inventory_access_list', kwargs={'pk': inventory.id}) + + response = get(url, user=admin_user, expect=200) + by_username = {} + for entry in response.data['results']: + by_username[entry['username']] = entry + assert 'sys-aud' in by_username + + assert len(by_username['sys-aud']['summary_fields']['indirect_access']) == 1 + assert len(by_username['sys-aud']['summary_fields']['direct_access']) == 0 + access_entry = by_username['sys-aud']['summary_fields']['indirect_access'][0] + assert access_entry['descendant_roles'] == ['read_role'] + + +@pytest.mark.django_db +def test_access_list_direct_access(get, admin_user, inventory): + u1 = User.objects.create(username='u1') + + inventory.admin_role.members.add(u1) + + url = reverse('api:inventory_access_list', kwargs={'pk': inventory.id}) + response = get(url, user=admin_user, expect=200) + by_username = {} + for entry in response.data['results']: + by_username[entry['username']] = entry + assert 'u1' in by_username + + assert len(by_username['u1']['summary_fields']['direct_access']) == 1 + assert len(by_username['u1']['summary_fields']['indirect_access']) == 0 + access_entry = by_username['u1']['summary_fields']['direct_access'][0] + assert sorted(access_entry['descendant_roles']) == sorted(['adhoc_role', 'use_role', 'update_role', 'read_role', 'admin_role']) + + +@pytest.mark.django_db +def test_access_list_organization_access(get, admin_user, inventory): + u2 = User.objects.create(username='u2') + + inventory.organization.inventory_admin_role.members.add(u2) + + # User has indirect access to the inventory + url = reverse('api:inventory_access_list', kwargs={'pk': inventory.id}) + response = get(url, user=admin_user, expect=200) + by_username = {} + for entry in response.data['results']: + by_username[entry['username']] = entry + assert 'u2' in by_username + + assert len(by_username['u2']['summary_fields']['indirect_access']) == 1 + assert len(by_username['u2']['summary_fields']['direct_access']) == 0 + access_entry = by_username['u2']['summary_fields']['indirect_access'][0] + assert sorted(access_entry['descendant_roles']) == sorted(['adhoc_role', 'use_role', 'update_role', 'read_role', 'admin_role']) + + # Test that user shows up in the organization access list with direct access of expected roles + url = reverse('api:organization_access_list', kwargs={'pk': inventory.organization_id}) + response = get(url, user=admin_user, expect=200) + by_username = {} + for entry in response.data['results']: + by_username[entry['username']] = entry + assert 'u2' in by_username + + assert len(by_username['u2']['summary_fields']['direct_access']) == 1 + assert len(by_username['u2']['summary_fields']['indirect_access']) == 0 + access_entry = by_username['u2']['summary_fields']['direct_access'][0] + assert sorted(access_entry['descendant_roles']) == sorted(['inventory_admin_role', 'read_role']) + + +@pytest.mark.django_db +def test_team_indirect_access(get, team, admin_user, inventory): + u1 = User.objects.create(username='u1') + team.member_role.members.add(u1) + + inventory.organization.inventory_admin_role.parents.add(team.member_role) + + url = reverse('api:inventory_access_list', kwargs={'pk': inventory.id}) + response = get(url, user=admin_user, expect=200) + by_username = {} + for entry in response.data['results']: + by_username[entry['username']] = entry + assert 'u1' in by_username + + assert len(by_username['u1']['summary_fields']['direct_access']) == 1 + assert len(by_username['u1']['summary_fields']['indirect_access']) == 0 + access_entry = by_username['u1']['summary_fields']['direct_access'][0] + assert sorted(access_entry['descendant_roles']) == sorted(['adhoc_role', 'use_role', 'update_role', 'read_role', 'admin_role']) + + +@pytest.mark.django_db +def test_workflow_access_list(workflow_job_template, alice, bob, setup_managed_roles, get, admin_user): + """Basic verification that WFJT access_list is functional""" + workflow_job_template.admin_role.members.add(alice) + workflow_job_template.organization.workflow_admin_role.members.add(bob) + + url = reverse('api:workflow_job_template_access_list', kwargs={'pk': workflow_job_template.pk}) + for u in (alice, bob, admin_user): + response = get(url, user=u, expect=200) + user_ids = [item['id'] for item in response.data['results']] + assert alice.pk in user_ids + assert bob.pk in user_ids diff --git a/awx/main/tests/functional/dab_rbac/test_access_regressions.py b/awx/main/tests/functional/dab_rbac/test_access_regressions.py new file mode 100644 index 000000000000..abd334269709 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_access_regressions.py @@ -0,0 +1,41 @@ +import pytest + +from awx.main.access import InstanceGroupAccess, NotificationTemplateAccess + +from ansible_base.rbac.models import RoleDefinition + + +@pytest.mark.django_db +def test_instance_group_object_role_delete(rando, instance_group, setup_managed_roles): + """Basic functionality of IG object-level admin role function AAP-25506""" + rd = RoleDefinition.objects.get(name='InstanceGroup Admin') + rd.give_permission(rando, instance_group) + access = InstanceGroupAccess(rando) + assert access.can_delete(instance_group) + + +@pytest.mark.django_db +def test_notification_template_object_role_change(rando, notification_template, setup_managed_roles): + """Basic functionality of NT object-level admin role function AAP-25493""" + rd = RoleDefinition.objects.get(name='NotificationTemplate Admin') + rd.give_permission(rando, notification_template) + access = NotificationTemplateAccess(rando) + assert access.can_change(notification_template, {'name': 'new name'}) + + +@pytest.mark.django_db +def test_organization_auditor_role(rando, setup_managed_roles, organization, inventory, project, jt_linked): + obj_list = (inventory, project, jt_linked) + for obj in obj_list: + assert obj.organization == organization, obj # sanity + + assert [rando.has_obj_perm(obj, 'view') for obj in obj_list] == [False for i in range(3)], obj_list + + rd = RoleDefinition.objects.get(name='Organization Audit') + rd.give_permission(rando, organization) + + codename_set = set(rd.permissions.values_list('codename', flat=True)) + assert not ({'view_inventory', 'view_jobtemplate', 'audit_organization'} - codename_set) # sanity + + assert [obj in type(obj).access_qs(rando) for obj in obj_list] == [True for i in range(3)], obj_list + assert [rando.has_obj_perm(obj, 'view') for obj in obj_list] == [True for i in range(3)], obj_list diff --git a/awx/main/tests/functional/dab_rbac/test_consolidate_teams.py b/awx/main/tests/functional/dab_rbac/test_consolidate_teams.py new file mode 100644 index 000000000000..1e42059e5664 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_consolidate_teams.py @@ -0,0 +1,147 @@ +import pytest + +from django.contrib.contenttypes.models import ContentType +from django.test import override_settings +from django.apps import apps + +from ansible_base.rbac.models import RoleDefinition, RoleUserAssignment, RoleTeamAssignment +from ansible_base.rbac.migrations._utils import give_permissions + +from awx.main.models import User, Team +from awx.main.migrations._dab_rbac import consolidate_indirect_user_roles + + +@pytest.mark.django_db +@override_settings(ANSIBLE_BASE_ALLOW_TEAM_PARENTS=True) +def test_consolidate_indirect_user_roles_with_nested_teams(setup_managed_roles, organization): + """ + Test the consolidate_indirect_user_roles function with a nested team hierarchy. + Setup: + - Users: A, B, C, D + - Teams: E, F, G + - Direct assignments: A→(E,F,G), B→E, C→F, D→G + - Team hierarchy: F→E (F is member of E), G→F (G is member of F) + Expected result after consolidation: + - Team E should have users: A, B, C, D (A directly, B directly, C through F, D through G→F) + - Team F should have users: A, C, D (A directly, C directly, D through G) + - Team G should have users: A, D (A directly, D directly) + """ + user_a = User.objects.create_user(username='user_a') + user_b = User.objects.create_user(username='user_b') + user_c = User.objects.create_user(username='user_c') + user_d = User.objects.create_user(username='user_d') + + team_e = Team.objects.create(name='Team E', organization=organization) + team_f = Team.objects.create(name='Team F', organization=organization) + team_g = Team.objects.create(name='Team G', organization=organization) + + # Get role definition and content type for give_permissions + team_member_role = RoleDefinition.objects.get(name='Team Member') + team_content_type = ContentType.objects.get_for_model(Team) + + # Assign users to teams + give_permissions(apps=apps, rd=team_member_role, users=[user_a], object_id=team_e.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, users=[user_a], object_id=team_f.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, users=[user_a], object_id=team_g.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, users=[user_b], object_id=team_e.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, users=[user_c], object_id=team_f.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, users=[user_d], object_id=team_g.id, content_type_id=team_content_type.id) + + # Mirror user assignments in the old RBAC system because signals don't run in tests + team_e.member_role.members.add(user_a.id, user_b.id) + team_f.member_role.members.add(user_a.id, user_c.id) + team_g.member_role.members.add(user_a.id, user_d.id) + + # Setup team-to-team relationships + give_permissions(apps=apps, rd=team_member_role, teams=[team_f], object_id=team_e.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, teams=[team_g], object_id=team_f.id, content_type_id=team_content_type.id) + + # Verify initial direct assignments + team_e_users_before = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_e.id).values_list('user_id', flat=True)) + assert team_e_users_before == {user_a.id, user_b.id} + team_f_users_before = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_f.id).values_list('user_id', flat=True)) + assert team_f_users_before == {user_a.id, user_c.id} + team_g_users_before = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_g.id).values_list('user_id', flat=True)) + assert team_g_users_before == {user_a.id, user_d.id} + + # Verify team-to-team relationships exist + assert RoleTeamAssignment.objects.filter(role_definition=team_member_role, team=team_f, object_id=team_e.id).exists() + assert RoleTeamAssignment.objects.filter(role_definition=team_member_role, team=team_g, object_id=team_f.id).exists() + + # Run the consolidation function + consolidate_indirect_user_roles(apps, None) + + # Verify consolidation + team_e_users_after = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_e.id).values_list('user_id', flat=True)) + assert team_e_users_after == {user_a.id, user_b.id, user_c.id, user_d.id}, f"Team E should have users A, B, C, D but has {team_e_users_after}" + team_f_users_after = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_f.id).values_list('user_id', flat=True)) + assert team_f_users_after == {user_a.id, user_c.id, user_d.id}, f"Team F should have users A, C, D but has {team_f_users_after}" + team_g_users_after = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_g.id).values_list('user_id', flat=True)) + assert team_g_users_after == {user_a.id, user_d.id}, f"Team G should have users A, D but has {team_g_users_after}" + + # Verify team member changes are mirrored to the old RBAC system + assert team_e_users_after == set(team_e.member_role.members.all().values_list('id', flat=True)) + assert team_f_users_after == set(team_f.member_role.members.all().values_list('id', flat=True)) + assert team_g_users_after == set(team_g.member_role.members.all().values_list('id', flat=True)) + + # Verify team-to-team relationships are removed after consolidation + assert not RoleTeamAssignment.objects.filter( + role_definition=team_member_role, team=team_f, object_id=team_e.id + ).exists(), "Team-to-team relationship F→E should be removed" + assert not RoleTeamAssignment.objects.filter( + role_definition=team_member_role, team=team_g, object_id=team_f.id + ).exists(), "Team-to-team relationship G→F should be removed" + + +@pytest.mark.django_db +@override_settings(ANSIBLE_BASE_ALLOW_TEAM_PARENTS=True) +def test_consolidate_indirect_user_roles_no_team_relationships(setup_managed_roles, organization): + """ + Test that the function handles the case where there are no team-to-team relationships. + It should return early without making any changes. + """ + # Create a user and team with direct assignment + user = User.objects.create_user(username='test_user') + team = Team.objects.create(name='Test Team', organization=organization) + + team_member_role = RoleDefinition.objects.get(name='Team Member') + team_content_type = ContentType.objects.get_for_model(Team) + give_permissions(apps=apps, rd=team_member_role, users=[user], object_id=team.id, content_type_id=team_content_type.id) + + # Compare count of assignments before and after consolidation + assignments_before = RoleUserAssignment.objects.filter(role_definition=team_member_role).count() + consolidate_indirect_user_roles(apps, None) + assignments_after = RoleUserAssignment.objects.filter(role_definition=team_member_role).count() + + assert assignments_before == assignments_after, "Number of assignments should not change when there are no team-to-team relationships" + + +@pytest.mark.django_db +@override_settings(ANSIBLE_BASE_ALLOW_TEAM_PARENTS=True) +def test_consolidate_indirect_user_roles_circular_reference(setup_managed_roles, organization): + """ + Test that the function handles circular team references without infinite recursion. + """ + team_a = Team.objects.create(name='Team A', organization=organization) + team_b = Team.objects.create(name='Team B', organization=organization) + + # Create a user assigned to team A + user = User.objects.create_user(username='test_user') + + team_member_role = RoleDefinition.objects.get(name='Team Member') + team_content_type = ContentType.objects.get_for_model(Team) + give_permissions(apps=apps, rd=team_member_role, users=[user], object_id=team_a.id, content_type_id=team_content_type.id) + + # Create circular team relationships: A → B → A + give_permissions(apps=apps, rd=team_member_role, teams=[team_b], object_id=team_a.id, content_type_id=team_content_type.id) + give_permissions(apps=apps, rd=team_member_role, teams=[team_a], object_id=team_b.id, content_type_id=team_content_type.id) + + # Run the consolidation function - should not raise an exception + consolidate_indirect_user_roles(apps, None) + + # Both teams should have the user assigned + team_a_users = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_a.id).values_list('user_id', flat=True)) + team_b_users = set(RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_b.id).values_list('user_id', flat=True)) + + assert user.id in team_a_users, "User should be assigned to team A" + assert user.id in team_b_users, "User should be assigned to team B" diff --git a/awx/main/tests/functional/dab_rbac/test_dab_rbac_api.py b/awx/main/tests/functional/dab_rbac/test_dab_rbac_api.py new file mode 100644 index 000000000000..3b09272d8c84 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_dab_rbac_api.py @@ -0,0 +1,196 @@ +import pytest + +from django.urls import reverse as django_reverse + +from awx.api.versioning import reverse +from awx.main.models import JobTemplate, Inventory, Organization +from awx.main.access import JobTemplateAccess, WorkflowJobTemplateAccess + +from ansible_base.rbac.models import RoleDefinition +from ansible_base.rbac import permission_registry + + +@pytest.mark.django_db +def test_managed_roles_created(setup_managed_roles): + "Managed RoleDefinitions are created in post_migration signal, we expect to see them here" + for cls in (JobTemplate, Inventory): + ct = permission_registry.content_type_model.objects.get_for_model(cls) + rds = list(RoleDefinition.objects.filter(content_type=ct)) + assert len(rds) > 1 + assert f'{cls.__name__} Admin' in [rd.name for rd in rds] + for rd in rds: + assert rd.managed is True + + +@pytest.mark.django_db +def test_custom_read_role(admin_user, post, setup_managed_roles): + rd_url = django_reverse('roledefinition-list') + resp = post( + url=rd_url, + data={"name": "read role made for test", "content_type": "awx.inventory", "permissions": ['awx.view_inventory']}, + user=admin_user, + expect=201, + ) + rd_id = resp.data['id'] + rd = RoleDefinition.objects.get(id=rd_id) + assert rd.content_type == permission_registry.content_type_model.objects.get_for_model(Inventory) + + +@pytest.mark.django_db +def test_custom_system_roles_prohibited(admin_user, post): + rd_url = django_reverse('roledefinition-list') + resp = post(url=rd_url, data={"name": "read role made for test", "content_type": None, "permissions": ['awx.view_inventory']}, user=admin_user, expect=400) + assert 'System-wide roles are not enabled' in str(resp.data) + + +@pytest.mark.django_db +def test_assignment_to_invisible_user(admin_user, alice, rando, inventory, post, setup_managed_roles): + "Alice can not see rando, and so can not give them a role assignment" + rd = RoleDefinition.objects.get(name='Inventory Admin') + rd.give_permission(alice, inventory) + url = django_reverse('roleuserassignment-list') + r = post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": inventory.id}, user=alice, expect=400) + assert 'does not exist' in str(r.data) + assert not rando.has_obj_perm(inventory, 'change') + + +@pytest.mark.django_db +def test_assign_managed_role(admin_user, alice, rando, inventory, post, setup_managed_roles, organization): + rd = RoleDefinition.objects.get(name='Inventory Admin') + rd.give_permission(alice, inventory) + # When alice and rando are members of the same org, they can see each other + member_rd = RoleDefinition.objects.get(name='Organization Member') + for u in (alice, rando): + member_rd.give_permission(u, organization) + # Now that alice has full permissions to the inventory, and can see rando, she will give rando permission + url = django_reverse('roleuserassignment-list') + post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": inventory.id}, user=alice, expect=201) + assert rando.has_obj_perm(inventory, 'change') is True + + +@pytest.mark.django_db +def test_assign_custom_delete_role(admin_user, rando, inventory, delete, patch): + # TODO: just a delete_inventory, without change_inventory + rd, _ = RoleDefinition.objects.get_or_create( + name='inventory-delete', + permissions=['delete_inventory', 'view_inventory', 'change_inventory'], + content_type=permission_registry.content_type_model.objects.get_for_model(Inventory), + ) + rd.give_permission(rando, inventory) + inv_id = inventory.pk + inv_url = reverse('api:inventory_detail', kwargs={'pk': inv_id}) + # TODO: eventually this will be valid test, for now ignore + # patch(url=inv_url, data={"description": "new"}, user=rando, expect=403) + delete(url=inv_url, user=rando, expect=202) + assert Inventory.objects.get(id=inv_id).pending_deletion + + +@pytest.mark.django_db +def test_assign_custom_add_role(admin_user, rando, organization, post, setup_managed_roles): + rd, _ = RoleDefinition.objects.get_or_create( + name='inventory-add', + permissions=['add_inventory', 'view_organization'], + content_type=permission_registry.content_type_model.objects.get_for_model(Organization), + ) + rd.give_permission(rando, organization) + url = reverse('api:inventory_list') + r = post(url=url, data={'name': 'abc', 'organization': organization.id}, user=rando, expect=201) + inv_id = r.data['id'] + inventory = Inventory.objects.get(id=inv_id) + assert rando.has_obj_perm(inventory, 'change') + + +@pytest.mark.django_db +def test_jt_creation_permissions(setup_managed_roles, inventory, project, rando): + """This tests that if you assign someone required permissions in the new API + using the managed roles, then that works to give permissions to create a job template""" + inv_rd = RoleDefinition.objects.get(name='Inventory Admin') + proj_rd = RoleDefinition.objects.get(name='Project Admin') + # establish prior state + access = JobTemplateAccess(rando) + assert not access.can_add({'inventory': inventory.pk, 'project': project.pk, 'name': 'foo-jt'}) + + inv_rd.give_permission(rando, inventory) + proj_rd.give_permission(rando, project) + + assert access.can_add({'inventory': inventory.pk, 'project': project.pk, 'name': 'foo-jt'}) + + +@pytest.mark.django_db +def test_workflow_creation_permissions(setup_managed_roles, organization, workflow_job_template, rando): + """Similar to JT, assigning new roles gives creator permissions""" + org_wf_rd = RoleDefinition.objects.get(name='Organization WorkflowJobTemplate Admin') + assert workflow_job_template.organization == organization # sanity + # establish prior state + access = WorkflowJobTemplateAccess(rando) + assert not access.can_add({'name': 'foo-flow', 'organization': organization.pk}) + org_wf_rd.give_permission(rando, organization) + + assert access.can_add({'name': 'foo-flow', 'organization': organization.pk}) + + +@pytest.mark.django_db +def test_assign_credential_to_user_of_another_org(setup_managed_roles, credential, admin_user, rando, org_admin, organization, post): + '''Test that a credential can only be assigned to a user in the same organization''' + # cannot assign credential to rando, as rando is not in the same org as the credential + rd = RoleDefinition.objects.get(name="Credential Admin") + credential.organization = organization + credential.save(update_fields=['organization']) + assert credential.organization not in Organization.access_qs(rando, 'member') + url = django_reverse('roleuserassignment-list') + resp = post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=400) + assert "You cannot grant credential access to a User not in the credentials' organization" in str(resp.data) + + # can assign credential to superuser + rando.is_superuser = True + rando.save() + post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=201) + + # can assign credential to org_admin + assert credential.organization in Organization.access_qs(org_admin, 'member') + post(url=url, data={"user": org_admin.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=201) + + +@pytest.mark.django_db +def test_adding_user_to_org_member_role(setup_managed_roles, organization, admin, bob, post, get): + ''' + Adding user to organization member role via the legacy RBAC endpoints + should give them access to the organization detail + ''' + url_detail = reverse('api:organization_detail', kwargs={'pk': organization.id}) + get(url_detail, user=bob, expect=403) + + role = organization.member_role + url = reverse('api:role_users_list', kwargs={'pk': role.id}) + post(url, data={'id': bob.id}, user=admin, expect=204) + + get(url_detail, user=bob, expect=200) + + +@pytest.mark.django_db +@pytest.mark.parametrize('actor', ['user', 'team']) +@pytest.mark.parametrize('role_name', ['Organization Admin', 'Organization Member', 'Team Admin', 'Team Member']) +def test_adding_actor_to_platform_roles(setup_managed_roles, role_name, actor, organization, team, admin, bob, post): + ''' + Allow user to be added to platform-level roles + Exceptions: + - Team cannot be added to Organization Member or Admin role + - Team cannot be added to Team Admin or Team Member role + ''' + if actor == 'team': + expect = 400 + else: + expect = 201 + rd = RoleDefinition.objects.get(name=role_name) + endpoint = 'roleuserassignment-list' if actor == 'user' else 'roleteamassignment-list' + url = django_reverse(endpoint) + object_id = team.id if 'Team' in role_name else organization.id + data = {'object_id': object_id, 'role_definition': rd.id} + actor_id = bob.id if actor == 'user' else team.id + data[actor] = actor_id + r = post(url, data=data, user=admin, expect=expect) + if expect == 400: + if 'Organization' in role_name: + assert 'Assigning organization member permission to teams is not allowed' in str(r.data) + if 'Team' in role_name: + assert 'Assigning team permissions to other teams is not allowed' in str(r.data) diff --git a/awx/main/tests/functional/dab_rbac/test_external_auditor.py b/awx/main/tests/functional/dab_rbac/test_external_auditor.py new file mode 100644 index 000000000000..c3602d736627 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_external_auditor.py @@ -0,0 +1,120 @@ +import pytest + +from django.apps import apps + +from ansible_base.rbac.managed import SystemAuditor +from ansible_base.rbac import permission_registry + +from awx.main.access import check_user_access, get_user_queryset +from awx.main.models import User, AdHocCommandEvent +from awx.api.versioning import reverse + + +@pytest.fixture +def ext_auditor_rd(): + info = SystemAuditor(overrides={'name': 'Alien Auditor', 'shortname': 'ext_auditor'}) + rd, _ = info.get_or_create(apps) + return rd + + +@pytest.fixture +def ext_auditor(ext_auditor_rd): + u = User.objects.create(username='external-auditor-user') + ext_auditor_rd.give_global_permission(u) + return u + + +@pytest.fixture +def obj_factory(request): + def _rf(fixture_name): + obj = request.getfixturevalue(fixture_name) + + # special case to make obj organization-scoped + if obj._meta.model_name == 'executionenvironment': + obj.organization = request.getfixturevalue('organization') + obj.save(update_fields=['organization']) + + return obj + + return _rf + + +@pytest.mark.django_db +def test_access_qs_external_auditor(ext_auditor_rd, rando, job_template): + ext_auditor_rd.give_global_permission(rando) + jt_cls = apps.get_model('main', 'JobTemplate') + ujt_cls = apps.get_model('main', 'UnifiedJobTemplate') + assert job_template in jt_cls.access_qs(rando) + assert job_template.id in jt_cls.access_ids_qs(rando) + assert job_template.id in ujt_cls.accessible_pk_qs(rando, 'read_role') + + +@pytest.mark.django_db +@pytest.mark.parametrize('model', sorted(permission_registry.all_registered_models, key=lambda cls: cls._meta.model_name)) +class TestExternalAuditorRoleAllModels: + def test_access_can_read_method(self, obj_factory, model, ext_auditor, rando): + fixture_name = model._meta.verbose_name.replace(' ', '_') + obj = obj_factory(fixture_name) + + assert check_user_access(rando, model, 'read', obj) is False + assert check_user_access(ext_auditor, model, 'read', obj) is True + + def test_access_get_queryset(self, obj_factory, model, ext_auditor, rando): + fixture_name = model._meta.verbose_name.replace(' ', '_') + obj = obj_factory(fixture_name) + + assert obj not in get_user_queryset(rando, model) + assert obj in get_user_queryset(ext_auditor, model) + + def test_global_list(self, obj_factory, model, ext_auditor, rando, get): + fixture_name = model._meta.verbose_name.replace(' ', '_') + obj_factory(fixture_name) + + url = reverse(f'api:{fixture_name}_list') + r = get(url, user=rando, expect=200) + initial_ct = r.data['count'] + + r = get(url, user=ext_auditor, expect=200) + assert r.data['count'] == initial_ct + 1 + + if fixture_name in ('job_template', 'workflow_job_template'): + url = reverse('api:unified_job_template_list') + r = get(url, user=rando, expect=200) + initial_ct = r.data['count'] + + r = get(url, user=ext_auditor, expect=200) + assert r.data['count'] == initial_ct + 1 + + def test_detail_view(self, obj_factory, model, ext_auditor, rando, get): + fixture_name = model._meta.verbose_name.replace(' ', '_') + obj = obj_factory(fixture_name) + + url = reverse(f'api:{fixture_name}_detail', kwargs={'pk': obj.pk}) + get(url, user=rando, expect=403) # NOTE: should be 401 + get(url, user=ext_auditor, expect=200) + + +@pytest.mark.django_db +class TestExternalAuditorNonRoleModels: + def test_ad_hoc_command_view(self, ad_hoc_command_factory, rando, ext_auditor, get): + """The AdHocCommandAccess class references is_system_auditor + + this is to prove it works with other system-level view roles""" + ad_hoc_command = ad_hoc_command_factory() + url = reverse('api:ad_hoc_command_list') + r = get(url, user=rando, expect=200) + assert r.data['count'] == 0 + r = get(url, user=ext_auditor, expect=200) + assert r.data['count'] == 1 + assert r.data['results'][0]['id'] == ad_hoc_command.id + + event = AdHocCommandEvent.objects.create(ad_hoc_command=ad_hoc_command) + url = reverse('api:ad_hoc_command_ad_hoc_command_events_list', kwargs={'pk': ad_hoc_command.id}) + r = get(url, user=rando, expect=403) + r = get(url, user=ext_auditor, expect=200) + assert r.data['count'] == 1 + + url = reverse('api:ad_hoc_command_event_detail', kwargs={'pk': event.id}) + r = get(url, user=rando, expect=403) + r = get(url, user=ext_auditor, expect=200) + assert r.data['id'] == event.id diff --git a/awx/main/tests/functional/dab_rbac/test_managed_roles.py b/awx/main/tests/functional/dab_rbac/test_managed_roles.py new file mode 100644 index 000000000000..82fd661fa593 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_managed_roles.py @@ -0,0 +1,56 @@ +import pytest + +from ansible_base.rbac.models import RoleDefinition, DABPermission, RoleUserAssignment + + +@pytest.mark.django_db +def test_roles_to_not_create(setup_managed_roles): + assert RoleDefinition.objects.filter(name='Organization Admin').count() == 1 + + SHOULD_NOT_EXIST = ('Organization Organization Admin', 'Organization Team Admin', 'Organization InstanceGroup Admin') + + bad_rds = RoleDefinition.objects.filter(name__in=SHOULD_NOT_EXIST) + if bad_rds.exists(): + bad_names = list(bad_rds.values_list('name', flat=True)) + raise Exception(f'Found RoleDefinitions that should not exist: {bad_names}') + + +@pytest.mark.django_db +def test_org_admin_role(setup_managed_roles): + rd = RoleDefinition.objects.get(name='Organization Admin') + codenames = list(rd.permissions.values_list('codename', flat=True)) + assert 'view_inventory' in codenames + assert 'change_inventory' in codenames + + +@pytest.mark.django_db +def test_project_update_role(setup_managed_roles): + """Role to allow updating a project on the object-level should exist""" + assert RoleDefinition.objects.filter(name='Project Update').count() == 1 + + +@pytest.mark.django_db +def test_org_child_add_permission(setup_managed_roles): + for model_name in ('Project', 'NotificationTemplate', 'WorkflowJobTemplate', 'Inventory'): + rd = RoleDefinition.objects.get(name=f'Organization {model_name} Admin') + assert 'add_' in str(rd.permissions.values_list('codename', flat=True)), f'The {rd.name} role definition expected to contain add_ permissions' + + # special case for JobTemplate, anyone can create one with use permission to project/inventory + assert not DABPermission.objects.filter(codename='add_jobtemplate').exists() + + +@pytest.mark.django_db +@pytest.mark.parametrize('resource_name', ['Team', 'Organization']) +@pytest.mark.parametrize('action', ['Member', 'Admin']) +def test_legacy_RBAC_uses_platform_roles(setup_managed_roles, resource_name, action, team, bob, organization): + ''' + Assignment to legacy RBAC roles should use platform role definitions + e.g. Team Admin, Team Member, Organization Member, Organization Admin + ''' + resource = team if resource_name == 'Team' else organization + if action == 'Member': + resource.member_role.members.add(bob) + else: + resource.admin_role.members.add(bob) + rd = RoleDefinition.objects.get(name=f'{resource_name} {action}') + assert RoleUserAssignment.objects.filter(role_definition=rd, user=bob, object_id=resource.id).exists() diff --git a/awx/main/tests/functional/dab_rbac/test_translation_layer.py b/awx/main/tests/functional/dab_rbac/test_translation_layer.py new file mode 100644 index 000000000000..98ee58f5b8c3 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_translation_layer.py @@ -0,0 +1,210 @@ +from unittest import mock +import json + +import pytest + +from crum import impersonate + +from awx.main.fields import ImplicitRoleField +from awx.main.models.rbac import get_role_from_object_role, give_creator_permissions, get_role_codenames, get_role_definition +from awx.main.models import User, Organization, WorkflowJobTemplate, WorkflowJobTemplateNode, Team +from awx.api.versioning import reverse + +from ansible_base.rbac.models import RoleUserAssignment, RoleDefinition +from ansible_base.rbac import permission_registry + + +@pytest.mark.django_db +@pytest.mark.parametrize( + 'role_name', + [ + 'execution_environment_admin_role', + 'workflow_admin_role', + 'project_admin_role', + 'admin_role', + 'auditor_role', + 'read_role', + 'execute_role', + 'approval_role', + 'notification_admin_role', + ], +) +def test_round_trip_roles(organization, rando, role_name, setup_managed_roles): + """ + Make an assignment with the old-style role, + get the equivelent new role + get the old role again + """ + getattr(organization, role_name).members.add(rando) + assignment = RoleUserAssignment.objects.get(user=rando) + old_role = get_role_from_object_role(assignment.object_role) + assert old_role.id == getattr(organization, role_name).id + + +@pytest.mark.django_db +@pytest.mark.parametrize('model', sorted(permission_registry.all_registered_models, key=lambda cls: cls._meta.model_name)) +def test_role_migration_matches(request, model, setup_managed_roles): + fixture_name = model._meta.verbose_name.replace(' ', '_') + obj = request.getfixturevalue(fixture_name) + role_ct = 0 + for field in obj._meta.get_fields(): + if isinstance(field, ImplicitRoleField): + if field.name == 'read_role': + continue # intentionally left as "Compat" roles + role_ct += 1 + old_role = getattr(obj, field.name) + old_codenames = set(get_role_codenames(old_role)) + rd = get_role_definition(old_role) + new_codenames = set(rd.permissions.values_list('codename', flat=True)) + # all the old roles should map to a non-Compat role definition + if 'Compat' not in rd.name: + model_rds = RoleDefinition.objects.filter(content_type=permission_registry.content_type_model.objects.get_for_model(obj)) + rd_data = {} + for rd in model_rds: + rd_data[rd.name] = list(rd.permissions.values_list('codename', flat=True)) + assert ( + 'Compat' not in rd.name + ), f'Permissions for old vs new roles did not match.\nold {field.name}: {old_codenames}\nnew:\n{json.dumps(rd_data, indent=2)}' + assert new_codenames == set(old_codenames) + + # In the old system these models did not have object-level roles, all others expect some model roles + if model._meta.model_name not in ('notificationtemplate', 'executionenvironment'): + assert role_ct > 0 + + +@pytest.mark.django_db +def test_role_naming(setup_managed_roles): + qs = RoleDefinition.objects.filter(content_type=permission_registry.content_type_model.objects.get(model='jobtemplate'), name__endswith='dmin') + assert qs.count() == 1 # sanity + rd = qs.first() + assert rd.name == 'JobTemplate Admin' + assert rd.description + assert rd.created_by is None + + +@pytest.mark.django_db +def test_action_role_naming(setup_managed_roles): + qs = RoleDefinition.objects.filter(content_type=permission_registry.content_type_model.objects.get(model='jobtemplate'), name__endswith='ecute') + assert qs.count() == 1 # sanity + rd = qs.first() + assert rd.name == 'JobTemplate Execute' + assert rd.description + assert rd.created_by is None + + +@pytest.mark.django_db +def test_compat_role_naming(setup_managed_roles, job_template, rando, alice): + with impersonate(alice): + job_template.read_role.members.add(rando) + qs = RoleDefinition.objects.filter(content_type=permission_registry.content_type_model.objects.get(model='jobtemplate'), name__endswith='ompat') + assert qs.count() == 1 # sanity + rd = qs.first() + assert rd.name == 'JobTemplate Read Compat' + assert rd.description + assert rd.created_by is None + + +@pytest.mark.django_db +def test_organization_admin_has_audit(setup_managed_roles): + """This formalizes a behavior change from old to new RBAC system + + Previously, the auditor_role did not list admin_role as a parent + this made various queries hard to deal with, requiring adding 2 conditions + The new system should explicitly list the auditor permission in org admin role""" + rd = RoleDefinition.objects.get(name='Organization Admin') + assert 'audit_organization' in rd.permissions.values_list('codename', flat=True) + + +@pytest.mark.django_db +def test_organization_level_permissions(organization, inventory, setup_managed_roles): + u1 = User.objects.create(username='alice') + u2 = User.objects.create(username='bob') + + organization.inventory_admin_role.members.add(u1) + organization.workflow_admin_role.members.add(u2) + + assert u1 in inventory.admin_role + assert u1 in organization.inventory_admin_role + assert u2 in organization.workflow_admin_role + + assert u2 not in organization.inventory_admin_role + assert u1 not in organization.workflow_admin_role + assert not (set(u1.has_roles.all()) & set(u2.has_roles.all())) # user have no roles in common + + # Old style + assert set(Organization.accessible_objects(u1, 'inventory_admin_role')) == set([organization]) + assert set(Organization.accessible_objects(u2, 'inventory_admin_role')) == set() + assert set(Organization.accessible_objects(u1, 'workflow_admin_role')) == set() + assert set(Organization.accessible_objects(u2, 'workflow_admin_role')) == set([organization]) + + # New style + assert set(Organization.access_qs(u1, 'add_inventory')) == set([organization]) + assert set(Organization.access_qs(u1, 'change_inventory')) == set([organization]) + assert set(Organization.access_qs(u2, 'add_inventory')) == set() + assert set(Organization.access_qs(u1, 'add_workflowjobtemplate')) == set() + assert set(Organization.access_qs(u2, 'add_workflowjobtemplate')) == set([organization]) + + +@pytest.mark.django_db +def test_organization_execute_role(organization, rando, setup_managed_roles): + organization.execute_role.members.add(rando) + assert rando in organization.execute_role + assert set(Organization.accessible_objects(rando, 'execute_role')) == set([organization]) + + +@pytest.mark.django_db +def test_workflow_approval_list(get, post, admin_user, setup_managed_roles): + workflow_job_template = WorkflowJobTemplate.objects.create() + approval_node = WorkflowJobTemplateNode.objects.create(workflow_job_template=workflow_job_template) + url = reverse('api:workflow_job_template_node_create_approval', kwargs={'pk': approval_node.pk, 'version': 'v2'}) + post(url, {'name': 'URL Test', 'description': 'An approval', 'timeout': 0}, user=admin_user) + approval_node.refresh_from_db() + approval_jt = approval_node.unified_job_template + approval_jt.create_unified_job() + + r = get(url=reverse('api:workflow_approval_list'), user=admin_user, expect=200) + assert r.data['count'] >= 1 + + +@pytest.mark.django_db +def test_creator_permission(rando, admin_user, inventory, setup_managed_roles): + give_creator_permissions(rando, inventory) + assert rando in inventory.admin_role + assert rando in inventory.admin_role.members.all() + + +@pytest.mark.django_db +def test_implicit_parents_no_assignments(organization): + """Through the normal course of creating models, we should not be changing DAB RBAC permissions""" + with mock.patch('awx.main.models.rbac.give_or_remove_permission') as mck: + Team.objects.create(name='random team', organization=organization) + mck.assert_not_called() + + +@pytest.mark.django_db +def test_user_auditor_rel(organization, rando, setup_managed_roles): + assert rando not in organization.auditor_role + audit_rd = RoleDefinition.objects.get(name='Organization Audit') + audit_rd.give_permission(rando, organization) + assert list(Organization.access_qs(rando, 'audit')) == [organization] + + +@pytest.mark.django_db +@pytest.mark.parametrize('resource_name', ['Organization', 'Team']) +@pytest.mark.parametrize('role_name', ['Member', 'Admin']) +def test_mapping_from_role_definitions_to_roles(organization, team, rando, role_name, resource_name, setup_managed_roles): + """ + ensure mappings for platform roles are correct + e.g. + Organization Member > organization.member_role + Organization Admin > organization.admin_role + Team Member > team.member_role + Team Admin > team.admin_role + """ + resource = organization if resource_name == 'Organization' else team + old_role_name = f"{role_name.lower()}_role" + getattr(resource, old_role_name).members.add(rando) + assignment = RoleUserAssignment.objects.get(user=rando) + assert assignment.role_definition.name == f'{resource_name} {role_name}' + old_role = get_role_from_object_role(assignment.object_role) + assert old_role.id == getattr(resource, old_role_name).id diff --git a/awx/main/tests/functional/dab_rbac/test_translation_layer_new_to_old.py b/awx/main/tests/functional/dab_rbac/test_translation_layer_new_to_old.py new file mode 100644 index 000000000000..92efef838769 --- /dev/null +++ b/awx/main/tests/functional/dab_rbac/test_translation_layer_new_to_old.py @@ -0,0 +1,80 @@ +from ansible_base.rbac.models import RoleDefinition, RoleUserAssignment, RoleTeamAssignment +from ansible_base.lib.utils.response import get_relative_url +import pytest + + +@pytest.mark.django_db +class TestNewToOld: + ''' + Tests that the DAB RBAC system is correctly translated to the old RBAC system + Namely, tests functionality of the _sync_assignments_to_old_rbac signal handler + ''' + + def test_new_to_old_rbac_addition(self, admin, post, inventory, bob, setup_managed_roles): + ''' + Assign user to Inventory Admin role definition, should be added to inventory.admin_role.members + ''' + rd = RoleDefinition.objects.get(name='Inventory Admin') + + url = get_relative_url('roleuserassignment-list') + post(url, user=admin, data={'role_definition': rd.id, 'user': bob.id, 'object_id': inventory.id}, expect=201) + assert bob in inventory.admin_role.members.all() + + def test_new_to_old_rbac_removal(self, admin, delete, inventory, bob, setup_managed_roles): + ''' + Remove user from Inventory Admin role definition, should be deleted from inventory.admin_role.members + ''' + inventory.admin_role.members.add(bob) + + rd = RoleDefinition.objects.get(name='Inventory Admin') + user_assignment = RoleUserAssignment.objects.get(user=bob, role_definition=rd, object_id=inventory.id) + + url = get_relative_url('roleuserassignment-detail', kwargs={'pk': user_assignment.id}) + delete(url, user=admin, expect=204) + assert bob not in inventory.admin_role.members.all() + + def test_new_to_old_rbac_team_member_addition(self, admin, post, team, bob, setup_managed_roles): + ''' + Assign user to Team Member role definition, should be added to team.member_role.members + ''' + rd = RoleDefinition.objects.get(name='Team Member') + + url = get_relative_url('roleuserassignment-list') + post(url, user=admin, data={'role_definition': rd.id, 'user': bob.id, 'object_id': team.id}, expect=201) + assert bob in team.member_role.members.all() + + def test_new_to_old_rbac_team_member_removal(self, admin, delete, team, bob, setup_managed_roles): + ''' + Remove user from Team Member role definition, should be deleted from team.member_role.members + ''' + team.member_role.members.add(bob) + + rd = RoleDefinition.objects.get(name='Team Member') + user_assignment = RoleUserAssignment.objects.get(user=bob, role_definition=rd, object_id=team.id) + + url = get_relative_url('roleuserassignment-detail', kwargs={'pk': user_assignment.id}) + delete(url, user=admin, expect=204) + assert bob not in team.member_role.members.all() + + def test_new_to_old_rbac_team_addition(self, admin, post, team, inventory, setup_managed_roles): + ''' + Assign team to Inventory Admin role definition, should be added to inventory.admin_role.parents + ''' + rd = RoleDefinition.objects.get(name='Inventory Admin') + + url = get_relative_url('roleteamassignment-list') + post(url, user=admin, data={'role_definition': rd.id, 'team': team.id, 'object_id': inventory.id}, expect=201) + assert team.member_role in inventory.admin_role.parents.all() + + def test_new_to_old_rbac_team_removal(self, admin, delete, team, inventory, setup_managed_roles): + ''' + Remove team from Inventory Admin role definition, should be deleted from inventory.admin_role.parents + ''' + inventory.admin_role.parents.add(team.member_role) + + rd = RoleDefinition.objects.get(name='Inventory Admin') + team_assignment = RoleTeamAssignment.objects.get(team=team, role_definition=rd, object_id=inventory.id) + + url = get_relative_url('roleteamassignment-detail', kwargs={'pk': team_assignment.id}) + delete(url, user=admin, expect=204) + assert team.member_role not in inventory.admin_role.parents.all() diff --git a/awx/main/tests/functional/dab_resource_registry/test_ansible_id_display.py b/awx/main/tests/functional/dab_resource_registry/test_ansible_id_display.py new file mode 100644 index 000000000000..bf0d5502628b --- /dev/null +++ b/awx/main/tests/functional/dab_resource_registry/test_ansible_id_display.py @@ -0,0 +1,39 @@ +import pytest + +from ansible_base.resource_registry.models import Resource + +from awx.api.versioning import reverse + + +def assert_has_resource(list_response, obj=None): + data = list_response.data + assert 'resource' in data['results'][0]['summary_fields'] + resource_data = data['results'][0]['summary_fields']['resource'] + assert resource_data['ansible_id'] + resource = Resource.objects.filter(ansible_id=resource_data['ansible_id']).first() + assert resource + assert resource.content_object + if obj: + objects = [Resource.objects.get(ansible_id=entry['summary_fields']['resource']['ansible_id']).content_object for entry in data['results']] + assert obj in objects + + +@pytest.mark.django_db +def test_organization_ansible_id(organization, admin_user, get): + url = reverse('api:organization_list') + response = get(url=url, user=admin_user, expect=200) + assert_has_resource(response, obj=organization) + + +@pytest.mark.django_db +def test_team_ansible_id(team, admin_user, get): + url = reverse('api:team_list') + response = get(url=url, user=admin_user, expect=200) + assert_has_resource(response, obj=team) + + +@pytest.mark.django_db +def test_user_ansible_id(rando, admin_user, get): + url = reverse('api:user_list') + response = get(url=url, user=admin_user, expect=200) + assert_has_resource(response, obj=rando) diff --git a/awx/main/tests/functional/dab_resource_registry/test_resource_list.py b/awx/main/tests/functional/dab_resource_registry/test_resource_list.py new file mode 100644 index 000000000000..b57030fb46bc --- /dev/null +++ b/awx/main/tests/functional/dab_resource_registry/test_resource_list.py @@ -0,0 +1,15 @@ +import pytest + +from ansible_base.lib.utils.response import get_relative_url + + +@pytest.mark.django_db +def test_users_in_resource_list(admin_user, rando, get): + url = get_relative_url("resource-list") + get(url=url, expect=200, user=admin_user) + + +@pytest.mark.django_db +def test_user_resource_detail(admin_user, rando, get): + url = get_relative_url("resource-detail", kwargs={'ansible_id': str(rando.resource.ansible_id)}) + get(url=url, expect=200, user=admin_user) diff --git a/awx/main/tests/functional/management/test_dispatcherd.py b/awx/main/tests/functional/management/test_dispatcherd.py new file mode 100644 index 000000000000..92f1d208d9a8 --- /dev/null +++ b/awx/main/tests/functional/management/test_dispatcherd.py @@ -0,0 +1,17 @@ +import pytest + +from awx.main.dispatch.config import get_dispatcherd_config +from awx.main.management.commands.dispatcherd import _hash_config + + +@pytest.mark.django_db +def test_dispatcherd_config_hash_is_stable(settings, monkeypatch): + monkeypatch.setenv('AWX_COMPONENT', 'dispatcher') + settings.CLUSTER_HOST_ID = 'test-node' + settings.JOB_EVENT_WORKERS = 1 + settings.DISPATCHER_SCHEDULE = {} + + config_one = get_dispatcherd_config(for_service=True) + config_two = get_dispatcherd_config(for_service=True) + + assert _hash_config(config_one) == _hash_config(config_two) diff --git a/awx/main/tests/functional/test_inventory_source_migration.py b/awx/main/tests/functional/migrations/test_inventory_source_migration.py similarity index 84% rename from awx/main/tests/functional/test_inventory_source_migration.py rename to awx/main/tests/functional/migrations/test_inventory_source_migration.py index 812b4b45b940..6d17e22936cf 100644 --- a/awx/main/tests/functional/test_inventory_source_migration.py +++ b/awx/main/tests/functional/migrations/test_inventory_source_migration.py @@ -31,15 +31,30 @@ def test_apply_new_instance_id(inventory_source): assert host2.instance_id == 'bad_user' -@pytest.mark.django_db -def test_cloudforms_inventory_removal(inventory): - ManagedCredentialType( +def cleanup_cloudforms(): + if 'cloudforms' in ManagedCredentialType.registry: + del ManagedCredentialType.registry['cloudforms'] + assert 'cloudforms' not in CredentialType.defaults + + +@pytest.fixture +def cloudforms_mct(): + ManagedCredentialType.registry['cloudforms'] = ManagedCredentialType( name='Red Hat CloudForms', namespace='cloudforms', kind='cloud', managed=True, inputs={}, + injectors={}, ) + yield + ManagedCredentialType.registry.pop('cloudforms', None) + + +@pytest.mark.django_db +def test_cloudforms_inventory_removal(request, inventory, cloudforms_mct): + request.addfinalizer(cleanup_cloudforms) + CredentialType.defaults['cloudforms']().save() cloudforms = CredentialType.objects.get(namespace='cloudforms') Credential.objects.create( diff --git a/awx/main/tests/functional/migrations/test_jt_rename_migration.py b/awx/main/tests/functional/migrations/test_jt_rename_migration.py new file mode 100644 index 000000000000..4d624c41be2c --- /dev/null +++ b/awx/main/tests/functional/migrations/test_jt_rename_migration.py @@ -0,0 +1,56 @@ +import pytest + +from awx.main.migrations._db_constraints import _rename_duplicates +from awx.main.models import JobTemplate + + +@pytest.mark.django_db +def test_rename_job_template_duplicates(organization, project): + ids = [] + for i in range(5): + jt = JobTemplate.objects.create(name=f'jt-{i}', organization=organization, project=project) + ids.append(jt.id) # saved in order of creation + + # Hack to first allow duplicate names of JT to test migration + JobTemplate.objects.filter(id__in=ids).update(org_unique=False) + + # Set all JTs to the same name + JobTemplate.objects.filter(id__in=ids).update(name='same_name_for_test') + + _rename_duplicates(JobTemplate) + + first_jt = JobTemplate.objects.get(id=ids[0]) + assert first_jt.name == 'same_name_for_test' + + for i, pk in enumerate(ids): + if i == 0: + continue + jt = JobTemplate.objects.get(id=pk) + # Name should be set based on creation order + assert jt.name == f'same_name_for_test_dup{i}' + + +@pytest.mark.django_db +def test_rename_job_template_name_too_long(organization, project): + ids = [] + for i in range(3): + jt = JobTemplate.objects.create(name=f'jt-{i}', organization=organization, project=project) + ids.append(jt.id) # saved in order of creation + + JobTemplate.objects.filter(id__in=ids).update(org_unique=False) + + chars = 512 + # Set all JTs to the same reaaaaaaly long name + JobTemplate.objects.filter(id__in=ids).update(name='A' * chars) + + _rename_duplicates(JobTemplate) + + first_jt = JobTemplate.objects.get(id=ids[0]) + assert first_jt.name == 'A' * chars + + for i, pk in enumerate(ids): + if i == 0: + continue + jt = JobTemplate.objects.get(id=pk) + assert jt.name.endswith(f'dup{i}') + assert len(jt.name) <= 512 diff --git a/awx/main/tests/functional/migrations/test_org_admin_migration.py b/awx/main/tests/functional/migrations/test_org_admin_migration.py new file mode 100644 index 000000000000..84bed9ac8839 --- /dev/null +++ b/awx/main/tests/functional/migrations/test_org_admin_migration.py @@ -0,0 +1,16 @@ +import pytest + +from django.apps import apps + +from awx.main.models import InstanceGroup +from awx.main.migrations import _OrgAdmin_to_use_ig as orgadmin + + +@pytest.mark.django_db +def test_migrate_admin_role(org_admin, organization): + instance_group = InstanceGroup.objects.create(name='test') + organization.admin_role.members.add(org_admin) + organization.instance_groups.add(instance_group) + orgadmin.migrate_org_admin_to_use(apps, None) + assert org_admin in instance_group.use_role.members.all() + assert instance_group.use_role.members.count() == 1 diff --git a/awx/main/tests/functional/migrations/test_rbac_migration.py b/awx/main/tests/functional/migrations/test_rbac_migration.py new file mode 100644 index 000000000000..8ee411ba1a5a --- /dev/null +++ b/awx/main/tests/functional/migrations/test_rbac_migration.py @@ -0,0 +1,49 @@ +import pytest + +from awx.main.migrations import _rbac as rbac +from awx.main.models import UnifiedJobTemplate, InventorySource, Inventory, JobTemplate, Project, Organization + + +@pytest.mark.django_db +def test_implied_organization_subquery_inventory(): + orgs = [] + for i in range(3): + orgs.append(Organization.objects.create(name='foo{}'.format(i))) + orgs.append(orgs[0]) + for i in range(4): + org = orgs[i] + if i == 2: + inventory = Inventory.objects.create(name='foo{}'.format(i)) + else: + inventory = Inventory.objects.create(name='foo{}'.format(i), organization=org) + inv_src = InventorySource.objects.create(name='foo{}'.format(i), inventory=inventory, source='ec2') + sources = UnifiedJobTemplate.objects.annotate(test_field=rbac.implicit_org_subquery(UnifiedJobTemplate, InventorySource)) + for inv_src in sources: + assert inv_src.test_field == inv_src.inventory.organization_id + + +@pytest.mark.django_db +def test_implied_organization_subquery_job_template(): + jts = [] + for i in range(5): + if i <= 3: + org = Organization.objects.create(name='foo{}'.format(i)) + else: + org = None + if i <= 4: + proj = Project.objects.create(name='foo{}'.format(i), organization=org) + else: + proj = None + jts.append(JobTemplate.objects.create(name='foo{}'.format(i), project=proj)) + # test case of sharing same org + jts[2].project.organization = jts[3].project.organization + jts[2].save() + ujts = UnifiedJobTemplate.objects.annotate(test_field=rbac.implicit_org_subquery(UnifiedJobTemplate, JobTemplate)) + for jt in ujts: + if not isinstance(jt, JobTemplate): # some are projects + assert jt.test_field is None + else: + if jt.project is None: + assert jt.test_field is None + else: + assert jt.test_field == jt.project.organization_id diff --git a/awx/main/tests/functional/migrations/test_token_sjt_removal.py b/awx/main/tests/functional/migrations/test_token_sjt_removal.py new file mode 100644 index 000000000000..35770fd4ca4e --- /dev/null +++ b/awx/main/tests/functional/migrations/test_token_sjt_removal.py @@ -0,0 +1,58 @@ +import pytest +from django.apps import apps +from django.utils.timezone import now + +from awx.main.migrations._create_system_jobs import delete_clear_tokens_sjt + +SJT_NAME = 'Cleanup Expired OAuth 2 Tokens' + + +def create_cleartokens_jt(apps, schema_editor): + # Deleted data migration + SystemJobTemplate = apps.get_model('main', 'SystemJobTemplate') + Schedule = apps.get_model('main', 'Schedule') + ContentType = apps.get_model('contenttypes', 'ContentType') + sjt_ct = ContentType.objects.get_for_model(SystemJobTemplate) + now_dt = now() + schedule_time = now_dt.strftime('%Y%m%dT%H%M%SZ') + + sjt, created = SystemJobTemplate.objects.get_or_create( + job_type='cleanup_tokens', + defaults=dict( + name=SJT_NAME, + description='Cleanup expired OAuth 2 access and refresh tokens', + polymorphic_ctype=sjt_ct, + created=now_dt, + modified=now_dt, + ), + ) + if created: + sched = Schedule( + name=SJT_NAME, + rrule='DTSTART:%s RRULE:FREQ=WEEKLY;INTERVAL=1' % schedule_time, + description='Removes expired OAuth 2 access and refresh tokens', + enabled=True, + created=now_dt, + modified=now_dt, + extra_data={}, + ) + sched.unified_job_template = sjt + sched.save() + + +@pytest.mark.django_db +def test_clear_token_sjt(): + SystemJobTemplate = apps.get_model('main', 'SystemJobTemplate') + Schedule = apps.get_model('main', 'Schedule') + create_cleartokens_jt(apps, None) + qs = SystemJobTemplate.objects.filter(name=SJT_NAME) + assert qs.count() == 1 + sjt = qs.first() + assert Schedule.objects.filter(unified_job_template=sjt).count() == 1 + assert Schedule.objects.filter(unified_job_template__systemjobtemplate__name=SJT_NAME).count() == 1 + + # Now run the migration logic to remove + delete_clear_tokens_sjt(apps, None) + assert SystemJobTemplate.objects.filter(name=SJT_NAME).count() == 0 + # Making sure that the schedule is cleaned up is the main point of this test + assert Schedule.objects.filter(unified_job_template__systemjobtemplate__name=SJT_NAME).count() == 0 diff --git a/awx/main/tests/functional/models/test_activity_stream.py b/awx/main/tests/functional/models/test_activity_stream.py index f8ae40b54054..8be052628d97 100644 --- a/awx/main/tests/functional/models/test_activity_stream.py +++ b/awx/main/tests/functional/models/test_activity_stream.py @@ -104,11 +104,13 @@ def test_auditor_is_recorded(self, post, value): else: assert len(entry_qs) == 1 # unfortunate, the original creation does _not_ set a real is_auditor field - assert 'is_system_auditor' not in json.loads(entry_qs[0].changes) + assert 'is_system_auditor' not in json.loads(entry_qs[0].changes) # NOTE: if this fails, see special note + # special note - if system auditor flag is moved to user model then we expect this assertion to be changed + # make sure that an extra entry is not created, expectation for count would change to 1 if value: - auditor_changes = json.loads(entry_qs[1].changes) - assert auditor_changes['object2'] == 'user' - assert auditor_changes['object2_pk'] == u.pk + entry = entry_qs[1] + assert json.loads(entry.changes) == {'is_system_auditor': [False, True]} + assert entry.object1 == 'user' def test_user_no_op_api(self, system_auditor): as_ct = ActivityStream.objects.count() diff --git a/awx/main/tests/functional/models/test_context_managers.py b/awx/main/tests/functional/models/test_context_managers.py index 9807d8a6e9cb..7dfc0da11729 100644 --- a/awx/main/tests/functional/models/test_context_managers.py +++ b/awx/main/tests/functional/models/test_context_managers.py @@ -1,7 +1,6 @@ import pytest # AWX context managers for testing -from awx.main.models.rbac import batch_role_ancestor_rebuilding from awx.main.signals import disable_activity_stream, disable_computed_fields, update_inventory_computed_fields # AWX models @@ -10,15 +9,6 @@ from awx.main.tests.functional import immediate_on_commit -@pytest.mark.django_db -def test_rbac_batch_rebuilding(rando, organization): - with batch_role_ancestor_rebuilding(): - organization.admin_role.members.add(rando) - inventory = organization.inventories.create(name='test-inventory') - assert rando not in inventory.admin_role - assert rando in inventory.admin_role - - @pytest.mark.django_db def test_disable_activity_stream(): with disable_activity_stream(): @@ -31,13 +21,13 @@ class TestComputedFields: def test_computed_fields_normal_use(self, mocker, inventory): job = Job.objects.create(name='fake-job', inventory=inventory) with immediate_on_commit(): - with mocker.patch.object(update_inventory_computed_fields, 'delay'): - job.delete() - update_inventory_computed_fields.delay.assert_called_once_with(inventory.id) + mocker.patch.object(update_inventory_computed_fields, 'delay') + job.delete() + update_inventory_computed_fields.delay.assert_called_once_with(inventory.id) def test_disable_computed_fields(self, mocker, inventory): job = Job.objects.create(name='fake-job', inventory=inventory) with disable_computed_fields(): - with mocker.patch.object(update_inventory_computed_fields, 'delay'): - job.delete() - update_inventory_computed_fields.delay.assert_not_called() + mocker.patch.object(update_inventory_computed_fields, 'delay') + job.delete() + update_inventory_computed_fields.delay.assert_not_called() diff --git a/awx/main/tests/functional/test_db_credential.py b/awx/main/tests/functional/models/test_db_credential.py similarity index 100% rename from awx/main/tests/functional/test_db_credential.py rename to awx/main/tests/functional/models/test_db_credential.py diff --git a/awx/main/tests/functional/models/test_events.py b/awx/main/tests/functional/models/test_events.py index 758e69b64146..51c1adf529ee 100644 --- a/awx/main/tests/functional/models/test_events.py +++ b/awx/main/tests/functional/models/test_events.py @@ -3,178 +3,212 @@ from django.utils.timezone import now -from awx.main.models import Job, JobEvent, Inventory, Host, JobHostSummary +from django.db.models import Q - -@pytest.mark.django_db -@mock.patch('awx.main.models.events.emit_event_detail') -def test_parent_changed(emit): - j = Job() - j.save() - JobEvent.create_from_data(job_id=j.pk, uuid='abc123', event='playbook_on_task_start').save() - assert JobEvent.objects.count() == 1 - for e in JobEvent.objects.all(): - assert e.changed is False - - JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event='runner_on_ok', event_data={'res': {'changed': ['localhost']}}).save() - # the `playbook_on_stats` event is where we update the parent changed linkage - JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event='playbook_on_stats').save() - events = JobEvent.objects.filter(event__in=['playbook_on_task_start', 'runner_on_ok']) - assert events.count() == 2 - for e in events.all(): - assert e.changed is True - - -@pytest.mark.django_db -@pytest.mark.parametrize('event', JobEvent.FAILED_EVENTS) -@mock.patch('awx.main.models.events.emit_event_detail') -def test_parent_failed(emit, event): - j = Job() - j.save() - JobEvent.create_from_data(job_id=j.pk, uuid='abc123', event='playbook_on_task_start').save() - assert JobEvent.objects.count() == 1 - for e in JobEvent.objects.all(): - assert e.failed is False - - JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event=event).save() - - # the `playbook_on_stats` event is where we update the parent failed linkage - JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event='playbook_on_stats').save() - events = JobEvent.objects.filter(event__in=['playbook_on_task_start', event]) - assert events.count() == 2 - for e in events.all(): - assert e.failed is True - - -@pytest.mark.django_db -def test_host_summary_generation(): - hostnames = [f'Host {i}' for i in range(100)] - inv = Inventory() - inv.save() - Host.objects.bulk_create([Host(created=now(), modified=now(), name=h, inventory_id=inv.id) for h in hostnames]) - j = Job(inventory=inv) - j.save() - host_map = dict((host.name, host.id) for host in inv.hosts.all()) - JobEvent.create_from_data( - job_id=j.pk, - parent_uuid='abc123', - event='playbook_on_stats', - event_data={ - 'ok': dict((hostname, len(hostname)) for hostname in hostnames), - 'changed': {}, - 'dark': {}, - 'failures': {}, - 'ignored': {}, - 'processed': {}, - 'rescued': {}, - 'skipped': {}, - }, - host_map=host_map, - ).save() - - assert j.job_host_summaries.count() == len(hostnames) - assert sorted([s.host_name for s in j.job_host_summaries.all()]) == sorted(hostnames) - - for s in j.job_host_summaries.all(): - assert host_map[s.host_name] == s.host_id - assert s.ok == len(s.host_name) - assert s.changed == 0 - assert s.dark == 0 - assert s.failures == 0 - assert s.ignored == 0 - assert s.processed == 0 - assert s.rescued == 0 - assert s.skipped == 0 - - for host in Host.objects.all(): - assert host.last_job_id == j.id - assert host.last_job_host_summary.host == host - - -@pytest.mark.django_db -def test_host_summary_generation_with_deleted_hosts(): - hostnames = [f'Host {i}' for i in range(10)] - inv = Inventory() - inv.save() - Host.objects.bulk_create([Host(created=now(), modified=now(), name=h, inventory_id=inv.id) for h in hostnames]) - j = Job(inventory=inv) - j.save() - host_map = dict((host.name, host.id) for host in inv.hosts.all()) - - # delete half of the hosts during the playbook run - for h in inv.hosts.all()[:5]: - h.delete() - - JobEvent.create_from_data( - job_id=j.pk, - parent_uuid='abc123', - event='playbook_on_stats', - event_data={ - 'ok': dict((hostname, len(hostname)) for hostname in hostnames), - 'changed': {}, - 'dark': {}, - 'failures': {}, - 'ignored': {}, - 'processed': {}, - 'rescued': {}, - 'skipped': {}, - }, - host_map=host_map, - ).save() - - ids = sorted([s.host_id or -1 for s in j.job_host_summaries.order_by('id').all()]) - names = sorted([s.host_name for s in j.job_host_summaries.all()]) - assert ids == [-1, -1, -1, -1, -1, 6, 7, 8, 9, 10] - assert names == ['Host 0', 'Host 1', 'Host 2', 'Host 3', 'Host 4', 'Host 5', 'Host 6', 'Host 7', 'Host 8', 'Host 9'] +from awx.main.models import Job, JobEvent, Inventory, Host, JobHostSummary, HostMetric @pytest.mark.django_db -def test_host_summary_generation_with_limit(): - # Make an inventory with 10 hosts, run a playbook with a --limit - # pointed at *one* host, - # Verify that *only* that host has an associated JobHostSummary and that - # *only* that host has an updated value for .last_job. - hostnames = [f'Host {i}' for i in range(10)] - inv = Inventory() - inv.save() - Host.objects.bulk_create([Host(created=now(), modified=now(), name=h, inventory_id=inv.id) for h in hostnames]) - j = Job(inventory=inv) - j.save() - - # host map is a data structure that tracks a mapping of host name --> ID - # for the inventory, _regardless_ of whether or not there's a limit - # applied to the actual playbook run - host_map = dict((host.name, host.id) for host in inv.hosts.all()) - - # by making the playbook_on_stats *only* include Host 1, we're emulating - # the behavior of a `--limit=Host 1` - matching_host = Host.objects.get(name='Host 1') - JobEvent.create_from_data( - job_id=j.pk, +class TestEvents: + def setup_method(self): + self.hostnames = [] + self.host_map = dict() + self.inventory = None + self.job = None + + @mock.patch('awx.main.models.events.emit_event_detail') + def test_parent_changed(self, emit): + j = Job() + j.save() + JobEvent.create_from_data(job_id=j.pk, uuid='abc123', event='playbook_on_task_start').save() + assert JobEvent.objects.count() == 1 + for e in JobEvent.objects.all(): + assert e.changed is False + + JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event='runner_on_ok', event_data={'res': {'changed': ['localhost']}}).save() + # the `playbook_on_stats` event is where we update the parent changed linkage + JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event='playbook_on_stats').save() + events = JobEvent.objects.filter(event__in=['playbook_on_task_start', 'runner_on_ok']) + assert events.count() == 2 + for e in events.all(): + assert e.changed is True + + @pytest.mark.parametrize('event', JobEvent.FAILED_EVENTS) + @mock.patch('awx.main.models.events.emit_event_detail') + def test_parent_failed(self, emit, event): + j = Job() + j.save() + JobEvent.create_from_data(job_id=j.pk, uuid='abc123', event='playbook_on_task_start').save() + assert JobEvent.objects.count() == 1 + for e in JobEvent.objects.all(): + assert e.failed is False + + JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event=event).save() + + # the `playbook_on_stats` event is where we update the parent failed linkage + JobEvent.create_from_data(job_id=j.pk, parent_uuid='abc123', event='playbook_on_stats').save() + events = JobEvent.objects.filter(event__in=['playbook_on_task_start', event]) + assert events.count() == 2 + for e in events.all(): + assert e.failed is True + + def test_host_summary_generation(self): + self._generate_hosts(100) + self._create_job_event(ok=dict((hostname, len(hostname)) for hostname in self.hostnames)) + + assert self.job.job_host_summaries.count() == len(self.hostnames) + assert sorted([s.host_name for s in self.job.job_host_summaries.all()]) == sorted(self.hostnames) + + for s in self.job.job_host_summaries.all(): + assert self.host_map[s.host_name] == s.host_id + assert s.ok == len(s.host_name) + assert s.changed == 0 + assert s.dark == 0 + assert s.failures == 0 + assert s.ignored == 0 + assert s.processed == 0 + assert s.rescued == 0 + assert s.skipped == 0 + + for host in Host.objects.all(): + assert host.last_job_id == self.job.id + assert host.last_job_host_summary.host == host + + def test_host_summary_generation_with_deleted_hosts(self): + self._generate_hosts(10) + + # delete half of the hosts during the playbook run + for h in self.inventory.hosts.all()[:5]: + h.delete() + + self._create_job_event(ok=dict((hostname, len(hostname)) for hostname in self.hostnames)) + + ids = sorted([s.host_id or -1 for s in self.job.job_host_summaries.order_by('id').all()]) + names = sorted([s.host_name for s in self.job.job_host_summaries.all()]) + assert ids == [-1, -1, -1, -1, -1, 6, 7, 8, 9, 10] + assert names == ['Host 0', 'Host 1', 'Host 2', 'Host 3', 'Host 4', 'Host 5', 'Host 6', 'Host 7', 'Host 8', 'Host 9'] + + def test_host_summary_generation_with_limit(self): + # Make an inventory with 10 hosts, run a playbook with a --limit + # pointed at *one* host, + # Verify that *only* that host has an associated JobHostSummary and that + # *only* that host has an updated value for .last_job. + self._generate_hosts(10) + + # by making the playbook_on_stats *only* include Host 1, we're emulating + # the behavior of a `--limit=Host 1` + matching_host = Host.objects.get(name='Host 1') + self._create_job_event(ok={matching_host.name: len(matching_host.name)}) # effectively, limit=Host 1 + + # since the playbook_on_stats only references one host, + # there should *only* be on JobHostSummary record (and it should + # be related to the appropriate Host) + assert JobHostSummary.objects.count() == 1 + for h in Host.objects.all(): + if h.name == 'Host 1': + assert h.last_job_id == self.job.id + assert h.last_job_host_summary_id == JobHostSummary.objects.first().id + else: + # all other hosts in the inventory should remain untouched + assert h.last_job_id is None + assert h.last_job_host_summary_id is None + + def test_host_metrics_insert(self): + self._generate_hosts(10) + + self._create_job_event( + ok=dict((hostname, len(hostname)) for hostname in self.hostnames[0:3]), + failures=dict((hostname, len(hostname)) for hostname in self.hostnames[3:6]), + processed=dict((hostname, len(hostname)) for hostname in self.hostnames[6:9]), + skipped=dict((hostname, len(hostname)) for hostname in [self.hostnames[9]]), + ) + + metrics = HostMetric.objects.all() + assert len(metrics) == 10 + for hm in metrics: + assert hm.automated_counter == 1 + assert hm.last_automation is not None + assert hm.deleted is False + + def test_host_metrics_update(self): + self._generate_hosts(12) + + self._create_job_event(ok=dict((hostname, len(hostname)) for hostname in self.hostnames)) + + # Soft delete 6 of the 12 host metrics, every even host like "Host 2" or "Host 4" + for host_name in self.hostnames[::2]: + hm = HostMetric.objects.get(hostname=host_name.lower()) + hm.soft_delete() + + assert len(HostMetric.objects.filter(Q(deleted=False) & Q(deleted_counter=0) & Q(last_deleted__isnull=True))) == 6 + assert len(HostMetric.objects.filter(Q(deleted=True) & Q(deleted_counter=1) & Q(last_deleted__isnull=False))) == 6 + + # hostnames in 'ignored' and 'rescued' stats are ignored + self.job = Job(inventory=self.inventory) + self.job.save() + self._create_job_event( + ignored=dict((hostname, len(hostname)) for hostname in self.hostnames[0:6]), + rescued=dict((hostname, len(hostname)) for hostname in self.hostnames[6:11]), + ) + + assert len(HostMetric.objects.filter(Q(deleted=False) & Q(deleted_counter=0) & Q(last_deleted__isnull=True))) == 6 + assert len(HostMetric.objects.filter(Q(deleted=True) & Q(deleted_counter=1) & Q(last_deleted__isnull=False))) == 6 + + # hostnames in 'changed', 'dark', 'failures', 'ok', 'processed', 'skipped' are processed + self.job = Job(inventory=self.inventory) + self.job.save() + self._create_job_event( + changed=dict((hostname, len(hostname)) for hostname in self.hostnames[0:2]), + dark=dict((hostname, len(hostname)) for hostname in self.hostnames[2:4]), + failures=dict((hostname, len(hostname)) for hostname in self.hostnames[4:6]), + ok=dict((hostname, len(hostname)) for hostname in self.hostnames[6:8]), + processed=dict((hostname, len(hostname)) for hostname in self.hostnames[8:10]), + skipped=dict((hostname, len(hostname)) for hostname in self.hostnames[10:12]), + ) + assert len(HostMetric.objects.filter(Q(deleted=False) & Q(deleted_counter=0) & Q(last_deleted__isnull=True))) == 6 + + # one of those 6 hosts is dark, so will not be counted + assert len(HostMetric.objects.filter(Q(deleted=False) & Q(deleted_counter=1) & Q(last_deleted__isnull=False))) == 5 + + def _generate_hosts(self, cnt, id_from=0): + self.hostnames = [f'Host {i}' for i in range(id_from, id_from + cnt)] + self.inventory = Inventory() + self.inventory.save() + Host.objects.bulk_create([Host(created=now(), modified=now(), name=h, inventory_id=self.inventory.id) for h in self.hostnames]) + self.job = Job(inventory=self.inventory) + self.job.save() + + # host map is a data structure that tracks a mapping of host name --> ID + # for the inventory, _regardless_ of whether or not there's a limit + # applied to the actual playbook run + self.host_map = dict((host.name, host.id) for host in self.inventory.hosts.all()) + + def _create_job_event( + self, parent_uuid='abc123', event='playbook_on_stats', - event_data={ - 'ok': {matching_host.name: len(matching_host.name)}, # effectively, limit=Host 1 - 'changed': {}, - 'dark': {}, - 'failures': {}, - 'ignored': {}, - 'processed': {}, - 'rescued': {}, - 'skipped': {}, - }, - host_map=host_map, - ).save() - - # since the playbook_on_stats only references one host, - # there should *only* be on JobHostSummary record (and it should - # be related to the appropriate Host) - assert JobHostSummary.objects.count() == 1 - for h in Host.objects.all(): - if h.name == 'Host 1': - assert h.last_job_id == j.id - assert h.last_job_host_summary_id == JobHostSummary.objects.first().id - else: - # all other hosts in the inventory should remain untouched - assert h.last_job_id is None - assert h.last_job_host_summary_id is None + ok=None, + changed=None, + dark=None, + failures=None, + ignored=None, + processed=None, + rescued=None, + skipped=None, + ): + JobEvent.create_from_data( + job_id=self.job.pk, + parent_uuid=parent_uuid, + event=event, + event_data={ + 'ok': ok or {}, + 'changed': changed or {}, + 'dark': dark or {}, + 'failures': failures or {}, + 'ignored': ignored or {}, + 'processed': processed or {}, + 'rescued': rescued or {}, + 'skipped': skipped or {}, + }, + host_map=self.host_map, + ).save() diff --git a/awx/main/tests/functional/models/test_ha.py b/awx/main/tests/functional/models/test_ha.py new file mode 100644 index 000000000000..bcc627b89826 --- /dev/null +++ b/awx/main/tests/functional/models/test_ha.py @@ -0,0 +1,59 @@ +import pytest + +# AWX +from awx.main.ha import is_ha_environment +from awx.main.models.ha import Instance +from awx.main.utils.common import get_auto_max_workers + +# Django +from django.test.utils import override_settings + + +@pytest.mark.django_db +def test_multiple_hybrid_instances(): + for i in range(3): + Instance.objects.create(hostname=f'foo{i}', node_type='hybrid') + assert is_ha_environment() + + +@pytest.mark.django_db +def test_double_control_instances(): + for i in range(2): + Instance.objects.create(hostname=f'foo{i}', node_type='control') + assert is_ha_environment() + + +@pytest.mark.django_db +def test_mix_hybrid_control_instances(): + Instance.objects.create(hostname='control_node', node_type='control') + Instance.objects.create(hostname='hybrid_node', node_type='hybrid') + assert is_ha_environment() + + +@pytest.mark.django_db +def test_db_localhost(): + Instance.objects.create(hostname='foo', node_type='hybrid') + Instance.objects.create(hostname='bar', node_type='execution') + assert is_ha_environment() is False + + +@pytest.mark.django_db +@pytest.mark.parametrize( + 'settings', + [ + dict(SYSTEM_TASK_ABS_MEM='16Gi', SYSTEM_TASK_ABS_CPU='24', SYSTEM_TASK_FORKS_MEM=400, SYSTEM_TASK_FORKS_CPU=4), + dict(SYSTEM_TASK_ABS_MEM='124Gi', SYSTEM_TASK_ABS_CPU='2', SYSTEM_TASK_FORKS_MEM=None, SYSTEM_TASK_FORKS_CPU=None), + ], + ids=['cpu_dominated', 'memory_dominated'], +) +def test_dispatcher_max_workers_reserve(settings, fake_redis): + """This tests that the dispatcher max_workers matches instance capacity + + Assumes capacity_adjustment is 1, + plus reserve worker count + """ + with override_settings(**settings): + i = Instance.objects.create(hostname='test-1', node_type='hybrid') + i.local_health_check() + + assert get_auto_max_workers() == i.capacity + 7, (i.cpu, i.memory, i.cpu_capacity, i.mem_capacity) diff --git a/awx/main/tests/functional/models/test_host_metric.py b/awx/main/tests/functional/models/test_host_metric.py index 1f560e474fec..dad829543590 100644 --- a/awx/main/tests/functional/models/test_host_metric.py +++ b/awx/main/tests/functional/models/test_host_metric.py @@ -20,3 +20,53 @@ def test_host_metrics_generation(): date_today = now().strftime('%Y-%m-%d') result = HostMetric.objects.filter(first_automation__startswith=date_today).count() assert result == len(hostnames) + + +@pytest.mark.django_db +def test_soft_delete(): + hostnames = [f'Host to delete {i}' for i in range(2)] + current_time = now() + HostMetric.objects.bulk_create([HostMetric(hostname=h, last_automation=current_time, automated_counter=42) for h in hostnames]) + + hm = HostMetric.objects.get(hostname="Host to delete 0") + assert hm.last_deleted is None + + last_deleted = None + for _ in range(3): + # soft delete 1st + # 2nd/3rd delete don't have an effect + hm.soft_delete() + if last_deleted is None: + last_deleted = hm.last_deleted + + assert hm.deleted is True + assert hm.deleted_counter == 1 + assert hm.last_deleted == last_deleted + assert hm.automated_counter == 42 + + # 2nd record is not touched + hm = HostMetric.objects.get(hostname="Host to delete 1") + assert hm.deleted is False + assert hm.deleted_counter == 0 + assert hm.last_deleted is None + assert hm.automated_counter == 42 + + +@pytest.mark.django_db +def test_soft_restore(): + current_time = now() + HostMetric.objects.create(hostname="Host 1", last_automation=current_time, deleted=True) + HostMetric.objects.create(hostname="Host 2", last_automation=current_time, deleted=True, last_deleted=current_time) + HostMetric.objects.create(hostname="Host 3", last_automation=current_time, deleted=False, last_deleted=current_time) + HostMetric.objects.all().update(automated_counter=42, deleted_counter=10) + + # 1. deleted, last_deleted not null + for hm in HostMetric.objects.all(): + for _ in range(3): + hm.soft_restore() + assert hm.deleted is False + assert hm.automated_counter == 42 and hm.deleted_counter == 10 + if hm.hostname == "Host 1": + assert hm.last_deleted is None + else: + assert hm.last_deleted == current_time diff --git a/awx/main/tests/functional/models/test_inventory.py b/awx/main/tests/functional/models/test_inventory.py index a1db473d3ed0..3a739a3b815e 100644 --- a/awx/main/tests/functional/models/test_inventory.py +++ b/awx/main/tests/functional/models/test_inventory.py @@ -5,8 +5,8 @@ # AWX from awx.main.models import Host, Inventory, InventorySource, InventoryUpdate, CredentialType, Credential, Job -from awx.main.constants import CLOUD_PROVIDERS from awx.main.utils.filters import SmartFilter +from awx.main.utils.plugins import discover_available_cloud_provider_plugin_names @pytest.mark.django_db @@ -166,10 +166,11 @@ def test_extra_credentials(self, project, credential): def test_all_cloud_sources_covered(self): """Code in several places relies on the fact that the older - CLOUD_PROVIDERS constant contains the same names as what are + discover_cloud_provider_plugin_names returns the same names as what are defined within the injectors """ - assert set(CLOUD_PROVIDERS) == set(InventorySource.injectors.keys()) + # slight exception case for constructed, because it has a FQCN but is not a cloud source + assert set(discover_available_cloud_provider_plugin_names()) | set(['constructed']) == set(InventorySource.injectors.keys()) @pytest.mark.parametrize('source,filename', [('ec2', 'aws_ec2.yml'), ('openstack', 'openstack.yml'), ('gce', 'gcp_compute.yml')]) def test_plugin_filenames(self, source, filename): @@ -192,6 +193,7 @@ def test_plugin_filenames(self, source, filename): ('satellite6', 'theforeman.foreman.foreman'), ('insights', 'redhatinsights.insights.insights'), ('controller', 'awx.awx.tower'), + ('terraform', 'cloud.terraform.terraform_state'), ], ) def test_plugin_proper_names(self, source, proper_name): @@ -270,6 +272,7 @@ def test_inventory_update_excessively_long_name(inventory, inventory_source): class TestHostManager: def test_host_filter_not_smart(self, setup_ec2_gce, organization): smart_inventory = Inventory(name='smart', organization=organization, host_filter='inventory_sources__source=ec2') + smart_inventory.save() assert len(smart_inventory.hosts.all()) == 0 def test_host_distinctness(self, setup_inventory_groups, organization): diff --git a/awx/main/tests/functional/models/test_job.py b/awx/main/tests/functional/models/test_job.py index e2ac17fb436f..3e15c1fc9e48 100644 --- a/awx/main/tests/functional/models/test_job.py +++ b/awx/main/tests/functional/models/test_job.py @@ -1,6 +1,6 @@ import pytest -from awx.main.models import JobTemplate, Job, JobHostSummary, WorkflowJob, Inventory, Project, Organization +from awx.main.models import JobTemplate, Job, JobHostSummary, WorkflowJob, Inventory, Host, Project, Organization @pytest.mark.django_db @@ -87,3 +87,47 @@ def test_slice_count_prompt_limited_by_inventory(self, job_template, inventory, unified_job = job_template.create_unified_job(job_slice_count=2) assert isinstance(unified_job, Job) + + +@pytest.mark.django_db +class TestGetSourceHostsForConstructedInventory: + """Tests for Job.get_source_hosts_for_constructed_inventory""" + + def test_returns_source_hosts_via_instance_id(self): + """Constructed hosts with instance_id pointing to source hosts are resolved correctly.""" + org = Organization.objects.create(name='test-org') + inv_input = Inventory.objects.create(organization=org, name='input-inv') + source_host1 = inv_input.hosts.create(name='host1') + source_host2 = inv_input.hosts.create(name='host2') + + inv_constructed = Inventory.objects.create(organization=org, name='constructed-inv', kind='constructed') + inv_constructed.input_inventories.add(inv_input) + Host.objects.create(inventory=inv_constructed, name='host1', instance_id=str(source_host1.id)) + Host.objects.create(inventory=inv_constructed, name='host2', instance_id=str(source_host2.id)) + + job = Job.objects.create(name='test-job', inventory=inv_constructed) + result = job.get_source_hosts_for_constructed_inventory() + + assert set(result.values_list('id', flat=True)) == {source_host1.id, source_host2.id} + + def test_no_inventory_returns_empty(self): + """A job with no inventory returns an empty queryset.""" + job = Job.objects.create(name='test-job') + result = job.get_source_hosts_for_constructed_inventory() + assert result.count() == 0 + + def test_ignores_hosts_without_instance_id(self): + """Hosts with empty instance_id are excluded from the result.""" + org = Organization.objects.create(name='test-org') + inv_input = Inventory.objects.create(organization=org, name='input-inv') + source_host = inv_input.hosts.create(name='host1') + + inv_constructed = Inventory.objects.create(organization=org, name='constructed-inv', kind='constructed') + inv_constructed.input_inventories.add(inv_input) + Host.objects.create(inventory=inv_constructed, name='host1', instance_id=str(source_host.id)) + Host.objects.create(inventory=inv_constructed, name='host-no-ref', instance_id='') + + job = Job.objects.create(name='test-job', inventory=inv_constructed) + result = job.get_source_hosts_for_constructed_inventory() + + assert list(result.values_list('id', flat=True)) == [source_host.id] diff --git a/awx/main/tests/functional/models/test_notifications.py b/awx/main/tests/functional/models/test_notifications.py index 2d1d5e0f1719..2c1d6022de45 100644 --- a/awx/main/tests/functional/models/test_notifications.py +++ b/awx/main/tests/functional/models/test_notifications.py @@ -98,7 +98,7 @@ def check_structure(self, expected_structure, obj): @pytest.mark.django_db @pytest.mark.parametrize('JobClass', [AdHocCommand, InventoryUpdate, Job, ProjectUpdate, SystemJob, WorkflowJob]) - def test_context(self, JobClass, sqlite_copy_expert, project, inventory_source): + def test_context(self, JobClass, sqlite_copy, project, inventory_source): """The Jinja context defines all of the fields that can be used by a template. Ensure that the context generated for each job type has the expected structure.""" kwargs = {} diff --git a/awx/main/tests/functional/models/test_schedule.py b/awx/main/tests/functional/models/test_schedule.py index 6ad711537386..97051ae45f0e 100644 --- a/awx/main/tests/functional/models/test_schedule.py +++ b/awx/main/tests/functional/models/test_schedule.py @@ -1,11 +1,11 @@ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from contextlib import contextmanager +from zoneinfo import ZoneInfo from django.utils.timezone import now from django.db.utils import IntegrityError from unittest import mock import pytest -import pytz from awx.main.models import JobTemplate, Schedule, ActivityStream @@ -76,7 +76,7 @@ def test_computed_fields_time_change(self, job_template): with self.assert_no_unwanted_stuff(s): # force update of next_run, as if schedule re-calculation had not happened # since this time - old_next_run = datetime(2009, 3, 13, tzinfo=pytz.utc) + old_next_run = datetime(2009, 3, 13, tzinfo=timezone.utc) Schedule.objects.filter(pk=s.pk).update(next_run=old_next_run) s.next_run = old_next_run prior_modified = s.modified @@ -121,30 +121,16 @@ def test_computed_fields_turning_off_by_deleting(self, job_template): assert job_template.next_schedule == expected_schedule -@pytest.mark.django_db -@pytest.mark.parametrize('freq, delta', (('MINUTELY', 1), ('HOURLY', 1))) -def test_past_week_rrule(job_template, freq, delta): - # see: https://github.com/ansible/awx/issues/8071 - recent = datetime.utcnow() - timedelta(days=3) - recent = recent.replace(hour=0, minute=0, second=0, microsecond=0) - recent_dt = recent.strftime('%Y%m%d') - rrule = f'DTSTART;TZID=America/New_York:{recent_dt}T000000 RRULE:FREQ={freq};INTERVAL={delta};COUNT=5' # noqa - sched = Schedule.objects.create(name='example schedule', rrule=rrule, unified_job_template=job_template) - first_event = sched.rrulestr(sched.rrule)[0] - assert first_event.replace(tzinfo=None) == recent - - @pytest.mark.django_db @pytest.mark.parametrize('freq, delta', (('MINUTELY', 1), ('HOURLY', 1))) def test_really_old_dtstart(job_template, freq, delta): # see: https://github.com/ansible/awx/issues/8071 # If an event is per-minute/per-hour and was created a *really long* - # time ago, we should just bump forward to start counting "in the last week" + # time ago, we should just bump forward the dtstart rrule = f'DTSTART;TZID=America/New_York:20150101T000000 RRULE:FREQ={freq};INTERVAL={delta}' # noqa sched = Schedule.objects.create(name='example schedule', rrule=rrule, unified_job_template=job_template) - last_week = (datetime.utcnow() - timedelta(days=7)).date() first_event = sched.rrulestr(sched.rrule)[0] - assert last_week == first_event.date() + assert now() - first_event < timedelta(days=1) # the next few scheduled events should be the next minute/hour incremented next_five_events = list(sched.rrulestr(sched.rrule).xafter(now(), count=5)) @@ -273,7 +259,7 @@ def test_utc_until_in_the_past(job_template): @pytest.mark.django_db -@mock.patch('awx.main.models.schedules.now', lambda: datetime(2030, 3, 5, tzinfo=pytz.utc)) +@mock.patch('awx.main.models.schedules.now', lambda: datetime(2030, 3, 5, tzinfo=timezone.utc)) def test_dst_phantom_hour(job_template): # The DST period in the United States begins at 02:00 (2 am) local time, so # the hour from 2:00:00 to 2:59:59 does not exist in the night of the @@ -470,15 +456,15 @@ def test_skip_sundays(): RRULE:INTERVAL=1;FREQ=DAILY EXRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU ''' - timezone = pytz.timezone("America/New_York") - friday_apr_29th = datetime(2022, 4, 29, 0, 0, 0, 0, timezone) - monday_may_2nd = datetime(2022, 5, 2, 23, 59, 59, 999, timezone) + tz = ZoneInfo("America/New_York") + friday_apr_29th = datetime(2022, 4, 29, 0, 0, 0, 0, tz) + monday_may_2nd = datetime(2022, 5, 2, 23, 59, 59, 999, tz) ruleset = Schedule.rrulestr(rrule) gen = ruleset.between(friday_apr_29th, monday_may_2nd, True) # We should only get Fri, Sat and Mon (skipping Sunday) assert len(list(gen)) == 3 - saturday_night = datetime(2022, 4, 30, 23, 59, 59, 9999, timezone) - monday_morning = datetime(2022, 5, 2, 0, 0, 0, 0, timezone) + saturday_night = datetime(2022, 4, 30, 23, 59, 59, 9999, tz) + monday_morning = datetime(2022, 5, 2, 0, 0, 0, 0, tz) gen = ruleset.between(saturday_night, monday_morning, True) assert len(list(gen)) == 0 @@ -490,17 +476,17 @@ def test_skip_sundays(): [ pytest.param( 'DTSTART;TZID=America/New_York:20210310T150000 RRULE:INTERVAL=1;FREQ=DAILY;UNTIL=20210430T150000Z EXRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;COUNT=5', - datetime(2021, 4, 29, 19, 0, 0, tzinfo=pytz.utc), + datetime(2021, 4, 29, 19, 0, 0, tzinfo=timezone.utc), id="Single rule in rule set with UTC TZ aware until", ), pytest.param( 'DTSTART;TZID=America/New_York:20220310T150000 RRULE:INTERVAL=1;FREQ=DAILY;UNTIL=20220430T150000 EXRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;COUNT=5', - datetime(2022, 4, 30, 19, 0, tzinfo=pytz.utc), + datetime(2022, 4, 30, 19, 0, tzinfo=timezone.utc), id="Single rule in ruleset with naive until", ), pytest.param( 'DTSTART;TZID=America/New_York:20220310T150000 RRULE:INTERVAL=1;FREQ=DAILY;COUNT=4 EXRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=SU;COUNT=5', - datetime(2022, 3, 12, 20, 0, tzinfo=pytz.utc), + datetime(2022, 3, 12, 20, 0, tzinfo=timezone.utc), id="Single rule in ruleset with count", ), pytest.param( @@ -515,12 +501,12 @@ def test_skip_sundays(): ), pytest.param( 'DTSTART;TZID=America/New_York:20220310T150000 RRULE:INTERVAL=1;FREQ=DAILY;UNTIL=20220430T150000Z', - datetime(2022, 4, 29, 19, 0, tzinfo=pytz.utc), + datetime(2022, 4, 29, 19, 0, tzinfo=timezone.utc), id="Single rule in rule with UTZ TZ aware until", ), pytest.param( 'DTSTART;TZID=America/New_York:20220310T150000 RRULE:INTERVAL=1;FREQ=DAILY;UNTIL=20220430T150000', - datetime(2022, 4, 30, 19, 0, tzinfo=pytz.utc), + datetime(2022, 4, 30, 19, 0, tzinfo=timezone.utc), id="Single rule in rule with naive until", ), pytest.param( @@ -535,12 +521,12 @@ def test_skip_sundays(): ), pytest.param( 'DTSTART;TZID=America/New_York:20220310T150000 RRULE:INTERVAL=1;FREQ=DAILY;BYDAY=SU;UNTIL=20220430T1500Z RRULE:INTERVAL=1;FREQ=DAILY;BYDAY=MO;COUNT=4', - datetime(2022, 4, 24, 19, 0, tzinfo=pytz.utc), + datetime(2022, 4, 24, 19, 0, tzinfo=timezone.utc), id="Multi rule one with until and one with an count", ), pytest.param( 'DTSTART;TZID=America/New_York:20010430T1500 RRULE:INTERVAL=1;FREQ=DAILY;BYDAY=SU;COUNT=1', - datetime(2001, 5, 6, 19, 0, tzinfo=pytz.utc), + datetime(2001, 5, 6, 19, 0, tzinfo=timezone.utc), id="Rule with count but ends in the past", ), pytest.param( diff --git a/awx/main/tests/functional/models/test_unified_job.py b/awx/main/tests/functional/models/test_unified_job.py index 389ea731b91c..0618085cef5f 100644 --- a/awx/main/tests/functional/models/test_unified_job.py +++ b/awx/main/tests/functional/models/test_unified_job.py @@ -1,28 +1,23 @@ import itertools import pytest +from uuid import uuid4 # CRUM from crum import impersonate -# Django -from django.contrib.contenttypes.models import ContentType - # AWX -from awx.main.models import UnifiedJobTemplate, Job, JobTemplate, WorkflowJobTemplate, WorkflowApprovalTemplate, Project, WorkflowJob, Schedule, Credential +from awx.main.models import UnifiedJobTemplate, Job, JobTemplate, WorkflowJobTemplate, Project, WorkflowJob, Schedule, Credential from awx.api.versioning import reverse from awx.main.constants import JOB_VARIABLE_PREFIXES @pytest.mark.django_db -def test_subclass_types(rando): - assert set(UnifiedJobTemplate._submodels_with_roles()) == set( - [ - ContentType.objects.get_for_model(JobTemplate).id, - ContentType.objects.get_for_model(Project).id, - ContentType.objects.get_for_model(WorkflowJobTemplate).id, - ContentType.objects.get_for_model(WorkflowApprovalTemplate).id, - ] - ) +def test_subclass_types(): + assert set(UnifiedJobTemplate._submodels_with_roles()) == { + JobTemplate, + Project, + WorkflowJobTemplate, + } @pytest.mark.django_db @@ -39,6 +34,64 @@ def test_soft_unique_together(post, project, admin_user): assert 'combination already exists' in str(r.data) +@pytest.mark.django_db +class TestJobCancel: + """ + Coverage for UnifiedJob.cancel, focused on interaction with dispatcherd objects. + Using mocks for the dispatcherd objects, because tests by default use a no-op broker. + """ + + def test_cancel_sets_flag_and_clears_start_args(self, mocker): + job = Job.objects.create(status='running', name='foo-job', celery_task_id=str(uuid4()), controller_node='foo', start_args='{"secret": "value"}') + job.websocket_emit_status = mocker.MagicMock() + + assert job.can_cancel is True + assert job.cancel_flag is False + + job.cancel() + job.refresh_from_db() + + assert job.cancel_flag is True + assert job.start_args == '' + + def test_cancel_sets_job_explanation(self, mocker): + job = Job.objects.create(status='running', name='foo-job', celery_task_id=str(uuid4()), controller_node='foo') + job.websocket_emit_status = mocker.MagicMock() + job_explanation = 'giggity giggity' + + job.cancel(job_explanation=job_explanation) + job.refresh_from_db() + + assert job.job_explanation == job_explanation + + def test_cancel_sends_control_message(self, mocker): + celery_task_id = str(uuid4()) + job = Job.objects.create(status='running', name='foo-job', celery_task_id=celery_task_id, controller_node='foo') + job.websocket_emit_status = mocker.MagicMock() + control = mocker.MagicMock() + get_control = mocker.patch('awx.main.models.unified_jobs.get_control_from_settings', return_value=control) + + job.cancel() + + get_control.assert_called_once_with(default_publish_channel='foo') + control.control.assert_called_once_with('cancel', data={'uuid': celery_task_id}) + + def test_cancel_refreshes_task_id_before_sending_control(self, mocker): + job = Job.objects.create(status='pending', name='foo-job', celery_task_id='', controller_node='bar') + job.websocket_emit_status = mocker.MagicMock() + celery_task_id = str(uuid4()) + Job.objects.filter(pk=job.pk).update(status='running', celery_task_id=celery_task_id) + control = mocker.MagicMock() + get_control = mocker.patch('awx.main.models.unified_jobs.get_control_from_settings', return_value=control) + refresh_spy = mocker.spy(job, 'refresh_from_db') + + job.cancel() + + refresh_spy.assert_called_once_with(fields=['celery_task_id', 'controller_node']) + get_control.assert_called_once_with(default_publish_channel='bar') + control.control.assert_called_once_with('cancel', data={'uuid': celery_task_id}) + + @pytest.mark.django_db class TestCreateUnifiedJob: """ diff --git a/awx/main/tests/functional/models/test_workflow.py b/awx/main/tests/functional/models/test_workflow.py index a21fbaa73bc1..924b5346c550 100644 --- a/awx/main/tests/functional/models/test_workflow.py +++ b/awx/main/tests/functional/models/test_workflow.py @@ -377,7 +377,7 @@ def test_apply_workflow_job_prompts(self, workflow_job_template, wfjt_prompts, p assert workflow_job.scm_branch is None assert workflow_job.job_tags is None assert workflow_job.skip_tags is None - assert len(workflow_job.labels.all()) is 0 + assert len(workflow_job.labels.all()) == 0 # fields from prompts used workflow_job = workflow_job_template.create_unified_job(**prompts_data) diff --git a/awx/main/tests/functional/test_rbac_api.py b/awx/main/tests/functional/rbac/test_rbac_api.py similarity index 90% rename from awx/main/tests/functional/test_rbac_api.py rename to awx/main/tests/functional/rbac/test_rbac_api.py index b697ef3144c2..61e7425c4da1 100644 --- a/awx/main/tests/functional/test_rbac_api.py +++ b/awx/main/tests/functional/rbac/test_rbac_api.py @@ -3,7 +3,9 @@ from django.db import transaction from awx.api.versioning import reverse -from awx.main.models.rbac import Role, ROLE_SINGLETON_SYSTEM_ADMINISTRATOR +from awx.main.models.rbac import Role + +from django.test.utils import override_settings @pytest.fixture @@ -31,8 +33,6 @@ def test_get_roles_list_user(organization, inventory, team, get, user): 'Users can see all roles they have access to, but not all roles' this_user = user('user-test_get_roles_list_user') organization.member_role.members.add(this_user) - custom_role = Role.objects.create(role_field='custom_role-test_get_roles_list_user') - organization.member_role.children.add(custom_role) url = reverse('api:role_list') response = get(url, this_user) @@ -46,10 +46,8 @@ def test_get_roles_list_user(organization, inventory, team, get, user): for r in roles['results']: role_hash[r['id']] = r - assert Role.singleton(ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).id in role_hash assert organization.admin_role.id in role_hash assert organization.member_role.id in role_hash - assert custom_role.id in role_hash assert inventory.admin_role.id not in role_hash assert team.member_role.id not in role_hash @@ -57,7 +55,8 @@ def test_get_roles_list_user(organization, inventory, team, get, user): @pytest.mark.django_db def test_roles_visibility(get, organization, project, admin, alice, bob): - Role.singleton('system_auditor').members.add(alice) + alice.is_system_auditor = True + alice.save() assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=admin).data['count'] == 1 assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=alice).data['count'] == 1 assert get(reverse('api:role_list') + '?id=%d' % project.update_role.id, user=bob).data['count'] == 0 @@ -67,7 +66,8 @@ def test_roles_visibility(get, organization, project, admin, alice, bob): @pytest.mark.django_db def test_roles_filter_visibility(get, organization, project, admin, alice, bob): - Role.singleton('system_auditor').members.add(alice) + alice.is_system_auditor = True + alice.save() project.update_role.members.add(admin) assert get(reverse('api:user_roles_list', kwargs={'pk': admin.id}) + '?id=%d' % project.update_role.id, user=admin).data['count'] == 1 @@ -105,15 +105,6 @@ def test_cant_delete_role(delete, admin, inventory): # -@pytest.mark.django_db -def test_get_user_roles_list(get, admin): - url = reverse('api:user_roles_list', kwargs={'pk': admin.id}) - response = get(url, admin) - assert response.status_code == 200 - roles = response.data - assert roles['count'] > 0 # 'system_administrator' role if nothing else - - @pytest.mark.django_db def test_user_view_other_user_roles(organization, inventory, team, get, alice, bob): 'Users can see roles for other users, but only the roles that that user has access to see as well' @@ -141,7 +132,6 @@ def test_user_view_other_user_roles(organization, inventory, team, get, alice, b assert organization.admin_role.id in role_hash assert custom_role.id not in role_hash # doesn't show up in the user roles list, not an explicit grant - assert Role.singleton(ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).id not in role_hash assert inventory.admin_role.id not in role_hash assert team.member_role.id not in role_hash # alice can't see this @@ -197,6 +187,7 @@ def test_remove_role_from_user(role, post, admin): @pytest.mark.django_db +@override_settings(ANSIBLE_BASE_ALLOW_TEAM_ORG_ADMIN=True, ANSIBLE_BASE_ALLOW_TEAM_ORG_MEMBER=True) def test_get_teams_roles_list(get, team, organization, admin): team.member_role.children.add(organization.admin_role) url = reverse('api:team_roles_list', kwargs={'pk': team.id}) @@ -396,36 +387,6 @@ def test_remove_team_from_role(post, team, admin, role): assert role.parents.filter(id=team.member_role.id).count() == 0 -# -# /roles//parents/ -# - - -@pytest.mark.django_db -def test_role_parents(get, team, admin, role): - role.parents.add(team.member_role) - url = reverse('api:role_parents_list', kwargs={'pk': role.id}) - response = get(url, admin) - assert response.status_code == 200 - assert response.data['count'] == 1 - assert response.data['results'][0]['id'] == team.member_role.id - - -# -# /roles//children/ -# - - -@pytest.mark.django_db -def test_role_children(get, team, admin, role): - role.parents.add(team.member_role) - url = reverse('api:role_children_list', kwargs={'pk': team.member_role.id}) - response = get(url, admin) - assert response.status_code == 200 - assert response.data['count'] == 2 - assert response.data['results'][0]['id'] == role.id or response.data['results'][1]['id'] == role.id - - # # Generics # diff --git a/awx/main/tests/functional/test_rbac_credential.py b/awx/main/tests/functional/rbac/test_rbac_credential.py similarity index 100% rename from awx/main/tests/functional/test_rbac_credential.py rename to awx/main/tests/functional/rbac/test_rbac_credential.py diff --git a/awx/main/tests/functional/rbac/test_rbac_execution_environment.py b/awx/main/tests/functional/rbac/test_rbac_execution_environment.py new file mode 100644 index 000000000000..b6d98f073b9e --- /dev/null +++ b/awx/main/tests/functional/rbac/test_rbac_execution_environment.py @@ -0,0 +1,147 @@ +import pytest + +from awx.main.access import ExecutionEnvironmentAccess +from awx.main.models import ExecutionEnvironment, Organization, Team +from awx.main.models.rbac import get_role_codenames + +from awx.api.versioning import reverse +from django.urls import reverse as django_reverse + +from ansible_base.rbac.models import RoleDefinition +from ansible_base.rbac import permission_registry + + +@pytest.fixture +def ee_rd(): + return RoleDefinition.objects.create_from_permissions( + name='EE object admin', + permissions=['change_executionenvironment', 'delete_executionenvironment'], + content_type=permission_registry.content_type_model.objects.get_for_model(ExecutionEnvironment), + ) + + +@pytest.fixture +def org_ee_rd(): + return RoleDefinition.objects.create_from_permissions( + name='EE org admin', + permissions=['add_executionenvironment', 'change_executionenvironment', 'delete_executionenvironment', 'view_organization'], + content_type=permission_registry.content_type_model.objects.get_for_model(Organization), + ) + + +@pytest.mark.django_db +def test_old_ee_role_maps_to_correct_permissions(organization): + assert set(get_role_codenames(organization.execution_environment_admin_role)) == { + 'view_organization', + 'add_executionenvironment', + 'change_executionenvironment', + 'delete_executionenvironment', + } + + +@pytest.fixture +def org_ee(organization): + return ExecutionEnvironment.objects.create(name='some user ee', organization=organization) + + +@pytest.fixture +def check_user_capabilities(get, setup_managed_roles): + def _rf(user, obj, expected): + url = reverse('api:execution_environment_list') + r = get(url, user=user, expect=200) + for item in r.data['results']: + if item['id'] == obj.pk: + assert expected == item['summary_fields']['user_capabilities'] + break + else: + raise RuntimeError(f'Could not find expected object ({obj}) in EE list result: {r.data}') + + return _rf + + +# ___ begin tests ___ + + +@pytest.mark.django_db +def test_any_user_can_view_global_ee(control_plane_execution_environment, rando): + assert ExecutionEnvironmentAccess(rando).can_read(control_plane_execution_environment) + + +@pytest.mark.django_db +def test_managed_ee_not_assignable(control_plane_execution_environment, ee_rd, rando, admin_user, post): + url = django_reverse('roleuserassignment-list') + r = post(url, {'role_definition': ee_rd.pk, 'user': rando.id, 'object_id': control_plane_execution_environment.pk}, user=admin_user, expect=400) + assert 'Can not assign object roles to managed Execution Environment' in str(r.data) + + +@pytest.mark.django_db +def test_org_member_required_for_assignment(org_ee, ee_rd, rando, admin_user, post): + url = django_reverse('roleuserassignment-list') + r = post(url, {'role_definition': ee_rd.pk, 'user': rando.id, 'object_id': org_ee.pk}, user=admin_user, expect=400) + assert 'User must have view permission to Execution Environment organization' in str(r.data) + + +@pytest.mark.django_db +def test_team_can_have_permission(org_ee, ee_rd, rando, admin_user, post): + org2 = Organization.objects.create(name='a different team') + team = Team.objects.create(name='a team', organization=org2) + team.member_role.members.add(rando) + assert org_ee not in ExecutionEnvironmentAccess(rando).get_queryset() # user can not view the EE + + url = django_reverse('roleteamassignment-list') + + # can give object roles to the team now + post(url, {'role_definition': ee_rd.pk, 'team': team.id, 'object_id': org_ee.pk}, user=admin_user, expect=201) + assert rando.has_obj_perm(org_ee, 'change') + assert org_ee in ExecutionEnvironmentAccess(rando).get_queryset() # user can view the EE now + + +@pytest.mark.django_db +def test_give_object_permission_to_ee(setup_managed_roles, org_ee, ee_rd, org_member, check_user_capabilities): + access = ExecutionEnvironmentAccess(org_member) + assert access.can_read(org_ee) # by virtue of being an org member + assert not access.can_change(org_ee, {'name': 'new'}) + check_user_capabilities(org_member, org_ee, {'edit': False, 'delete': False, 'copy': False}) + + ee_rd.give_permission(org_member, org_ee) + assert access.can_change(org_ee, {'name': 'new', 'organization': org_ee.organization.id}) + + check_user_capabilities(org_member, org_ee, {'edit': True, 'delete': True, 'copy': False}) + + +@pytest.mark.django_db +def test_need_related_organization_access(org_ee, ee_rd, org_member): + org2 = Organization.objects.create(name='another organization') + ee_rd.give_permission(org_member, org_ee) + org2.member_role.members.add(org_member) + access = ExecutionEnvironmentAccess(org_member) + assert access.can_change(org_ee, {'name': 'new', 'organization': org_ee.organization}) + assert access.can_change(org_ee, {'name': 'new', 'organization': org_ee.organization.id}) + assert not access.can_change(org_ee, {'name': 'new', 'organization': org2.id}) + assert not access.can_change(org_ee, {'name': 'new', 'organization': org2}) + + # User can make the change if they have relevant permission to the new organization + org_ee.organization.execution_environment_admin_role.members.add(org_member) + org2.execution_environment_admin_role.members.add(org_member) + assert access.can_change(org_ee, {'name': 'new', 'organization': org2.id}) + assert access.can_change(org_ee, {'name': 'new', 'organization': org2}) + + +@pytest.mark.django_db +@pytest.mark.parametrize('style', ['new', 'old']) +def test_give_org_permission_to_ee(setup_managed_roles, org_ee, organization, org_member, check_user_capabilities, style, org_ee_rd): + access = ExecutionEnvironmentAccess(org_member) + assert not access.can_change(org_ee, {'name': 'new'}) + check_user_capabilities(org_member, org_ee, {'edit': False, 'delete': False, 'copy': False}) + + if style == 'new': + org_ee_rd.give_permission(org_member, organization) + assert org_member.has_obj_perm(org_ee.organization, 'add_executionenvironment') # sanity + else: + organization.execution_environment_admin_role.members.add(org_member) + + assert access.can_change(org_ee, {'name': 'new', 'organization': organization.id}) + check_user_capabilities(org_member, org_ee, {'edit': True, 'delete': True, 'copy': True}) + + # Extra check, user can not remove the EE from the organization + assert not access.can_change(org_ee, {'name': 'new', 'organization': None}) diff --git a/awx/main/tests/functional/rbac/test_rbac_instance_groups.py b/awx/main/tests/functional/rbac/test_rbac_instance_groups.py new file mode 100644 index 000000000000..418e5a351a34 --- /dev/null +++ b/awx/main/tests/functional/rbac/test_rbac_instance_groups.py @@ -0,0 +1,117 @@ +import pytest + +from awx.main.access import ( + InstanceGroupAccess, + OrganizationAccess, + InventoryAccess, + JobTemplateAccess, +) + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "obj_perm,allowed,readonly,partial", [("admin_role", True, True, True), ("use_role", False, True, True), ("read_role", False, True, False)] +) +def test_ig_role_base_visibility(default_instance_group, rando, obj_perm, allowed, partial, readonly): + if obj_perm: + getattr(default_instance_group, obj_perm).members.add(rando) + + assert readonly == InstanceGroupAccess(rando).can_read(default_instance_group) + assert partial == InstanceGroupAccess(rando).can_use(default_instance_group) + assert not InstanceGroupAccess(rando).can_add(default_instance_group) + assert allowed == InstanceGroupAccess(rando).can_admin(default_instance_group) + assert allowed == InstanceGroupAccess(rando).can_change(default_instance_group, {'name': 'New Name'}) + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "obj_perm,subobj_perm,allowed", [('admin_role', 'use_role', True), ('admin_role', 'read_role', False), ('admin_role', 'admin_role', True)] +) +def test_ig_role_based_associability(default_instance_group, rando, organization, job_template_factory, obj_perm, subobj_perm, allowed): + objects = job_template_factory('jt', organization=organization, project='p', inventory='i', credential='c') + if obj_perm: + getattr(objects.job_template, obj_perm).members.add(rando) + getattr(objects.inventory, obj_perm).members.add(rando) + getattr(objects.organization, obj_perm).members.add(rando) + if subobj_perm: + getattr(default_instance_group, subobj_perm).members.add(rando) + + assert allowed == JobTemplateAccess(rando).can_attach(objects.job_template, default_instance_group, 'instance_groups', None) + assert allowed == InventoryAccess(rando).can_attach(objects.inventory, default_instance_group, 'instance_groups', None) + assert allowed == OrganizationAccess(rando).can_attach(objects.organization, default_instance_group, 'instance_groups', None) + + +@pytest.mark.django_db +def test_ig_use_with_org_admin(default_instance_group, rando, org_admin): + default_instance_group.use_role.members.add(rando) + + assert list(InstanceGroupAccess(org_admin).get_queryset()) != [default_instance_group] + assert list(InstanceGroupAccess(rando).get_queryset()) == [default_instance_group] + + +@pytest.mark.django_db +def test_ig_normal_user_visibility(organization, default_instance_group, user): + u = user('user', False) + assert len(InstanceGroupAccess(u).get_queryset()) == 0 + organization.instance_groups.add(default_instance_group) + organization.member_role.members.add(u) + assert len(InstanceGroupAccess(u).get_queryset()) == 0 + + +@pytest.mark.django_db +def test_ig_admin_user_visibility(organization, default_instance_group, admin, system_auditor, org_admin): + assert len(InstanceGroupAccess(admin).get_queryset()) == 1 + assert len(InstanceGroupAccess(system_auditor).get_queryset()) == 1 + assert len(InstanceGroupAccess(org_admin).get_queryset()) == 0 + organization.instance_groups.add(default_instance_group) + assert len(InstanceGroupAccess(org_admin).get_queryset()) == 0 + + +@pytest.mark.django_db +def test_ig_normal_user_associability(organization, default_instance_group, user): + u = user('user', False) + access = OrganizationAccess(u) + assert not access.can_attach(organization, default_instance_group, 'instance_groups', None) + organization.instance_groups.add(default_instance_group) + organization.member_role.members.add(u) + assert not access.can_attach(organization, default_instance_group, 'instance_groups', None) + + +@pytest.mark.django_db +def test_ig_associability(organization, default_instance_group, admin, system_auditor, org_admin, org_member, job_template_factory): + admin_access = OrganizationAccess(admin) + auditor_access = OrganizationAccess(system_auditor) + oadmin_access = OrganizationAccess(org_admin) + omember_access = OrganizationAccess(org_member) + assert admin_access.can_attach(organization, default_instance_group, 'instance_groups', None) + assert not oadmin_access.can_attach(organization, default_instance_group, 'instance_groups', None) + assert not auditor_access.can_attach(organization, default_instance_group, 'instance_groups', None) + assert not omember_access.can_attach(organization, default_instance_group, 'instance_groups', None) + + organization.instance_groups.add(default_instance_group) + + assert admin_access.can_unattach(organization, default_instance_group, 'instance_groups', None) + assert not oadmin_access.can_unattach(organization, default_instance_group, 'instance_groups', None) + assert not auditor_access.can_unattach(organization, default_instance_group, 'instance_groups', None) + assert not omember_access.can_unattach(organization, default_instance_group, 'instance_groups', None) + + objects = job_template_factory('jt', organization=organization, project='p', inventory='i', credential='c') + admin_access = InventoryAccess(admin) + auditor_access = InventoryAccess(system_auditor) + oadmin_access = InventoryAccess(org_admin) + omember_access = InventoryAccess(org_member) + + assert admin_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) + assert not oadmin_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) + assert not auditor_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) + assert not omember_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) + + admin_access = JobTemplateAccess(admin) + auditor_access = JobTemplateAccess(system_auditor) + oadmin_access = JobTemplateAccess(org_admin) + omember_access = JobTemplateAccess(org_member) + + assert admin_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) + assert not oadmin_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) + assert not auditor_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) + assert not omember_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) diff --git a/awx/main/tests/functional/test_rbac_inventory.py b/awx/main/tests/functional/rbac/test_rbac_inventory.py similarity index 100% rename from awx/main/tests/functional/test_rbac_inventory.py rename to awx/main/tests/functional/rbac/test_rbac_inventory.py diff --git a/awx/main/tests/functional/test_rbac_job.py b/awx/main/tests/functional/rbac/test_rbac_job.py similarity index 92% rename from awx/main/tests/functional/test_rbac_job.py rename to awx/main/tests/functional/rbac/test_rbac_job.py index ff5c6c25a255..c4bcee00d684 100644 --- a/awx/main/tests/functional/test_rbac_job.py +++ b/awx/main/tests/functional/rbac/test_rbac_job.py @@ -2,7 +2,15 @@ from rest_framework.exceptions import PermissionDenied -from awx.main.access import JobAccess, JobLaunchConfigAccess, AdHocCommandAccess, InventoryUpdateAccess, ProjectUpdateAccess +from awx.main.access import ( + JobAccess, + JobLaunchConfigAccess, + AdHocCommandAccess, + InventoryUpdateAccess, + ProjectUpdateAccess, + SystemJobTemplateAccess, + SystemJobAccess, +) from awx.main.models import ( Job, JobLaunchConfig, @@ -350,3 +358,26 @@ def test_can_use_minor(self, rando): assert access.can_use(config) assert rando.can_access(JobLaunchConfig, 'use', config) + + +@pytest.mark.django_db +class TestSystemJobTemplateAccess: + def test_system_job_template_auditor(self, system_auditor, system_job_template): + access = SystemJobTemplateAccess(system_auditor) + assert access.can_read(system_job_template) + assert not access.can_start(system_job_template) + + def test_system_job_template_rando(self, rando, system_job_template): + access = SystemJobTemplateAccess(rando) + assert not access.can_read(system_job_template) + assert not access.can_start(system_job_template) + + def test_system_job_template_superuser(self, admin_user, system_job_template): + access = SystemJobTemplateAccess(admin_user) + assert access.can_read(system_job_template) + assert access.can_start(system_job_template) + + def test_org_auditor_view_system_job(self, system_job_template, org_auditor): + system_job = system_job_template.create_unified_job() + access = SystemJobAccess(org_auditor) + assert not access.can_read(system_job) diff --git a/awx/main/tests/functional/test_rbac_job_start.py b/awx/main/tests/functional/rbac/test_rbac_job_start.py similarity index 100% rename from awx/main/tests/functional/test_rbac_job_start.py rename to awx/main/tests/functional/rbac/test_rbac_job_start.py diff --git a/awx/main/tests/functional/test_rbac_job_templates.py b/awx/main/tests/functional/rbac/test_rbac_job_templates.py similarity index 86% rename from awx/main/tests/functional/test_rbac_job_templates.py rename to awx/main/tests/functional/rbac/test_rbac_job_templates.py index bccec0a1c2e1..34f82d9a74b9 100644 --- a/awx/main/tests/functional/test_rbac_job_templates.py +++ b/awx/main/tests/functional/rbac/test_rbac_job_templates.py @@ -4,7 +4,7 @@ from awx.api.versioning import reverse from awx.main.access import BaseAccess, JobTemplateAccess, ScheduleAccess from awx.main.models.jobs import JobTemplate -from awx.main.models import Project, Organization, Inventory, Schedule, User +from awx.main.models import Project, Organization, Schedule @mock.patch.object(BaseAccess, 'check_license', return_value=None) @@ -165,7 +165,7 @@ def test_system_admin_orphan_capabilities(self, job_template, admin_user): @pytest.mark.django_db @pytest.mark.job_permissions -def test_job_template_creator_access(project, organization, rando, post): +def test_job_template_creator_access(project, organization, rando, post, setup_managed_roles): project.use_role.members.add(rando) response = post( url=reverse('api:job_template_list'), @@ -177,13 +177,19 @@ def test_job_template_creator_access(project, organization, rando, post): jt_pk = response.data['id'] jt_obj = JobTemplate.objects.get(pk=jt_pk) # Creating a JT should place the creator in the admin role - assert rando in jt_obj.admin_role.members.all() + assert rando in jt_obj.admin_role @pytest.mark.django_db @pytest.mark.job_permissions -@pytest.mark.parametrize('lacking', ['project', 'inventory']) -def test_job_template_insufficient_creator_permissions(lacking, project, inventory, organization, rando, post): +@pytest.mark.parametrize( + 'lacking,reason', + [ + ('project', 'You do not have use permission on Project'), + ('inventory', 'You do not have use permission on Inventory'), + ], +) +def test_job_template_insufficient_creator_permissions(lacking, reason, project, inventory, organization, rando, post): if lacking != 'project': project.use_role.members.add(rando) else: @@ -192,12 +198,13 @@ def test_job_template_insufficient_creator_permissions(lacking, project, invento inventory.use_role.members.add(rando) else: inventory.read_role.members.add(rando) - post( + response = post( url=reverse('api:job_template_list'), data=dict(name='newly-created-jt', inventory=inventory.id, project=project.pk, playbook='helloworld.yml'), user=rando, expect=403, ) + assert reason in response.data[lacking] @pytest.mark.django_db @@ -283,48 +290,3 @@ def test_orphan_JT_adoption(self, project, patch, admin_user, org_admin): assert org_admin not in jt.admin_role patch(url=jt.get_absolute_url(), data={'project': project.id}, user=admin_user, expect=200) assert org_admin in jt.admin_role - - def test_inventory_read_transfer_direct(self, patch): - orgs = [] - invs = [] - admins = [] - for i in range(2): - org = Organization.objects.create(name='org{}'.format(i)) - org_admin = User.objects.create(username='user{}'.format(i)) - inv = Inventory.objects.create(organization=org, name='inv{}'.format(i)) - org.auditor_role.members.add(org_admin) - - orgs.append(org) - admins.append(org_admin) - invs.append(inv) - - jt = JobTemplate.objects.create(name='foo', inventory=invs[0]) - assert admins[0] in jt.read_role - assert admins[1] not in jt.read_role - - jt.inventory = invs[1] - jt.save(update_fields=['inventory']) - assert admins[0] not in jt.read_role - assert admins[1] in jt.read_role - - def test_inventory_read_transfer_indirect(self, patch): - orgs = [] - admins = [] - for i in range(2): - org = Organization.objects.create(name='org{}'.format(i)) - org_admin = User.objects.create(username='user{}'.format(i)) - org.auditor_role.members.add(org_admin) - - orgs.append(org) - admins.append(org_admin) - - inv = Inventory.objects.create(organization=orgs[0], name='inv{}'.format(i)) - - jt = JobTemplate.objects.create(name='foo', inventory=inv) - assert admins[0] in jt.read_role - assert admins[1] not in jt.read_role - - inv.organization = orgs[1] - inv.save(update_fields=['organization']) - assert admins[0] not in jt.read_role - assert admins[1] in jt.read_role diff --git a/awx/main/tests/functional/test_rbac_label.py b/awx/main/tests/functional/rbac/test_rbac_label.py similarity index 100% rename from awx/main/tests/functional/test_rbac_label.py rename to awx/main/tests/functional/rbac/test_rbac_label.py diff --git a/awx/main/tests/functional/test_labels.py b/awx/main/tests/functional/rbac/test_rbac_labels.py similarity index 100% rename from awx/main/tests/functional/test_labels.py rename to awx/main/tests/functional/rbac/test_rbac_labels.py diff --git a/awx/main/tests/functional/test_rbac_notifications.py b/awx/main/tests/functional/rbac/test_rbac_notifications.py similarity index 98% rename from awx/main/tests/functional/test_rbac_notifications.py rename to awx/main/tests/functional/rbac/test_rbac_notifications.py index d05efa244c9b..72d5d016a954 100644 --- a/awx/main/tests/functional/test_rbac_notifications.py +++ b/awx/main/tests/functional/rbac/test_rbac_notifications.py @@ -99,7 +99,9 @@ def test_notification_template_access_org_user(notification_template, user): @pytest.mark.django_db def test_notificaiton_template_orphan_access_org_admin(notification_template, organization, org_admin): notification_template.organization = None + notification_template.save(update_fields=['organization']) access = NotificationTemplateAccess(org_admin) + assert not org_admin.has_obj_perm(notification_template, 'change') assert not access.can_change(notification_template, {'organization': organization.id}) diff --git a/awx/main/tests/functional/test_rbac_organization.py b/awx/main/tests/functional/rbac/test_rbac_organization.py similarity index 75% rename from awx/main/tests/functional/test_rbac_organization.py rename to awx/main/tests/functional/rbac/test_rbac_organization.py index ddb0692ea3ed..7a07225d3057 100644 --- a/awx/main/tests/functional/test_rbac_organization.py +++ b/awx/main/tests/functional/rbac/test_rbac_organization.py @@ -48,3 +48,17 @@ def test_org_resource_role(ext_auth, organization, rando, org_admin): assert access.can_attach(organization, rando, 'member_role.members') == ext_auth organization.member_role.members.add(rando) assert access.can_unattach(organization, rando, 'member_role.members') == ext_auth + + +@pytest.mark.django_db +def test_delete_org_while_workflow_active(workflow_job_template): + ''' + Delete org while workflow job is active (i.e. changing status) + ''' + assert workflow_job_template.organization # sanity check + wj = workflow_job_template.create_unified_job() # status should be new + workflow_job_template.organization.delete() + wj.refresh_from_db() + assert wj.status != 'pending' # sanity check + wj.status = 'pending' # status needs to change in order to trigger workflow_job_template.save() + wj.save(update_fields=['status']) diff --git a/awx/main/tests/functional/test_rbac_project.py b/awx/main/tests/functional/rbac/test_rbac_project.py similarity index 100% rename from awx/main/tests/functional/test_rbac_project.py rename to awx/main/tests/functional/rbac/test_rbac_project.py diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/rbac/test_rbac_role.py similarity index 100% rename from awx/main/tests/functional/test_rbac_role.py rename to awx/main/tests/functional/rbac/test_rbac_role.py diff --git a/awx/main/tests/functional/test_rbac_team.py b/awx/main/tests/functional/rbac/test_rbac_team.py similarity index 98% rename from awx/main/tests/functional/test_rbac_team.py rename to awx/main/tests/functional/rbac/test_rbac_team.py index a18a69a94bb3..6c3e68c6c1d7 100644 --- a/awx/main/tests/functional/test_rbac_team.py +++ b/awx/main/tests/functional/rbac/test_rbac_team.py @@ -92,7 +92,7 @@ def test_team_accessible_by(team, user, project): u = user('team_member', False) team.member_role.children.add(project.use_role) - assert team in project.read_role + assert list(Project.accessible_objects(team, 'read_role')) == [project] assert u not in project.read_role team.member_role.members.add(u) diff --git a/awx/main/tests/functional/test_rbac_user.py b/awx/main/tests/functional/rbac/test_rbac_user.py similarity index 78% rename from awx/main/tests/functional/test_rbac_user.py rename to awx/main/tests/functional/rbac/test_rbac_user.py index d5386343bd8f..10ca851bbe57 100644 --- a/awx/main/tests/functional/test_rbac_user.py +++ b/awx/main/tests/functional/rbac/test_rbac_user.py @@ -4,7 +4,7 @@ from django.test import TransactionTestCase from awx.main.access import UserAccess, RoleAccess, TeamAccess -from awx.main.models import User, Organization, Inventory, Role +from awx.main.models import User, Organization, Inventory, get_system_auditor_role class TestSysAuditorTransactional(TransactionTestCase): @@ -18,7 +18,8 @@ def inventory(self): def test_auditor_caching(self): rando = self.rando() - with self.assertNumQueries(1): + get_system_auditor_role() # pre-create role, normally done by migrations + with self.assertNumQueries(2): v = rando.is_system_auditor assert not v with self.assertNumQueries(0): @@ -122,25 +123,6 @@ def test_team_org_resource_role(ext_auth, organization, rando, org_admin, team): ] == [True for i in range(2)] -@pytest.mark.django_db -def test_user_accessible_objects(user, organization): - """ - We cannot directly use accessible_objects for User model because - both editing and read permissions are obligated to complex business logic - """ - admin = user('admin', False) - u = user('john', False) - access = UserAccess(admin) - assert access.get_queryset().count() == 1 # can only see himself - - organization.member_role.members.add(u) - organization.member_role.members.add(admin) - assert access.get_queryset().count() == 2 - - organization.member_role.members.remove(u) - assert access.get_queryset().count() == 1 - - @pytest.mark.django_db def test_org_admin_create_sys_auditor(org_admin): access = UserAccess(org_admin) @@ -172,34 +154,3 @@ def test_org_admin_cannot_delete_member_attached_to_other_group(org_admin, org_m access = UserAccess(org_admin) other_org.member_role.members.add(org_member) assert not access.can_delete(org_member) - - -@pytest.mark.parametrize('reverse', (True, False)) -@pytest.mark.django_db -def test_consistency_of_is_superuser_flag(reverse): - users = [User.objects.create(username='rando_{}'.format(i)) for i in range(2)] - for u in users: - assert u.is_superuser is False - - system_admin = Role.singleton('system_administrator') - if reverse: - for u in users: - u.roles.add(system_admin) - else: - system_admin.members.add(*[u.id for u in users]) # like .add(42, 54) - - for u in users: - u.refresh_from_db() - assert u.is_superuser is True - - users[0].roles.clear() - for u in users: - u.refresh_from_db() - assert users[0].is_superuser is False - assert users[1].is_superuser is True - - system_admin.members.clear() - - for u in users: - u.refresh_from_db() - assert u.is_superuser is False diff --git a/awx/main/tests/functional/test_rbac_workflow.py b/awx/main/tests/functional/rbac/test_rbac_workflow.py similarity index 88% rename from awx/main/tests/functional/test_rbac_workflow.py rename to awx/main/tests/functional/rbac/test_rbac_workflow.py index 4c29907519c1..c94f4f4df881 100644 --- a/awx/main/tests/functional/test_rbac_workflow.py +++ b/awx/main/tests/functional/rbac/test_rbac_workflow.py @@ -1,6 +1,7 @@ import pytest from awx.main.access import ( + UnifiedJobAccess, WorkflowJobTemplateAccess, WorkflowJobTemplateNodeAccess, WorkflowJobAccess, @@ -13,30 +14,6 @@ from awx.main.models import InventorySource, JobLaunchConfig -@pytest.fixture -def wfjt(workflow_job_template_factory, organization): - objects = workflow_job_template_factory('test_workflow', organization=organization, persisted=True) - return objects.workflow_job_template - - -@pytest.fixture -def wfjt_with_nodes(workflow_job_template_factory, organization, job_template): - objects = workflow_job_template_factory( - 'test_workflow', organization=organization, workflow_job_template_nodes=[{'unified_job_template': job_template}], persisted=True - ) - return objects.workflow_job_template - - -@pytest.fixture -def wfjt_node(wfjt_with_nodes): - return wfjt_with_nodes.workflow_job_template_nodes.all()[0] - - -@pytest.fixture -def workflow_job(wfjt): - return wfjt.workflow_jobs.create(name='test_workflow') - - @pytest.mark.django_db class TestWorkflowJobTemplateAccess: def test_random_user_no_edit(self, wfjt, rando): @@ -59,6 +36,13 @@ def test_org_workflow_admin_role_inheritance(self, wfjt, org_member): assert org_member in wfjt.execute_role assert org_member in wfjt.read_role + def test_non_super_admin_no_add_without_org(self, wfjt, organization, rando): + organization.member_role.members.add(rando) + wfjt.admin_role.members.add(rando) + access = WorkflowJobTemplateAccess(rando, save_messages=True) + assert not access.can_add({'name': 'without org'}) + assert 'An organization is required to create a workflow job template for normal user' in access.messages['organization'] + @pytest.mark.django_db class TestWorkflowJobTemplateNodeAccess: @@ -148,7 +132,7 @@ def test_attacher_permissions(self, wfjt_node, job_template, rando, add_wfjt_adm elif permission_type == 'instance_groups': sub_obj = InstanceGroup.objects.create() org = Organization.objects.create() - org.admin_role.members.add(rando) # only admins can see IGs + sub_obj.use_role.members.add(rando) # only admins can see IGs org.instance_groups.add(sub_obj) access = WorkflowJobTemplateNodeAccess(rando) @@ -262,6 +246,30 @@ def test_relaunch_inventory_access(self, workflow_job, inventory, rando): inventory.use_role.members.add(rando) assert WorkflowJobAccess(rando).can_start(workflow_job) + @pytest.mark.parametrize('org_role', ['admin_role', 'auditor_role']) + def test_workflow_job_org_audit_access(self, workflow_job_template, rando, org_role): + assert workflow_job_template.organization # sanity + workflow_job = workflow_job_template.create_unified_job() + assert workflow_job.organization # sanity + + assert not UnifiedJobAccess(rando).can_read(workflow_job) + assert not WorkflowJobAccess(rando).can_read(workflow_job) + assert workflow_job not in WorkflowJobAccess(rando).filtered_queryset() + + org = workflow_job.organization + role = getattr(org, org_role) + role.members.add(rando) + + assert UnifiedJobAccess(rando).can_read(workflow_job) + assert WorkflowJobAccess(rando).can_read(workflow_job) + assert workflow_job in WorkflowJobAccess(rando).filtered_queryset() + + # Organization-level permissions should persist after deleting the WFJT + workflow_job_template.delete() + assert UnifiedJobAccess(rando).can_read(workflow_job) + assert WorkflowJobAccess(rando).can_read(workflow_job) + assert workflow_job in WorkflowJobAccess(rando).filtered_queryset() + @pytest.mark.django_db class TestWFJTCopyAccess: diff --git a/awx/main/tests/functional/task_management/test_cancel_dependency.py b/awx/main/tests/functional/task_management/test_cancel_dependency.py new file mode 100644 index 000000000000..f8fb82a9fb8b --- /dev/null +++ b/awx/main/tests/functional/task_management/test_cancel_dependency.py @@ -0,0 +1,274 @@ +# Generated by Claude Opus 4.6 (claude-opus-4-6) +# +# Test file for cancel + dependency chain behavior and workflow cancel propagation. +# +# These tests verify: +# +# 1. TaskManager.process_job_dep_failures() correctly distinguishes canceled vs +# failed dependencies in the job_explanation message. +# +# 2. TaskManager.process_pending_tasks() transitions pending jobs with +# cancel_flag=True directly to canceled status. +# +# 3. WorkflowManager + TaskManager together cancel all spawned jobs in a +# workflow and finalize the workflow as canceled. + +import pytest +from unittest import mock + +from awx.main.scheduler import TaskManager, DependencyManager, WorkflowManager +from awx.main.models import JobTemplate, ProjectUpdate, WorkflowApproval, WorkflowJobTemplate +from awx.main.models.workflow import WorkflowApprovalTemplate +from . import create_job + + +@pytest.fixture +def scm_on_launch_objects(job_template_factory): + """Create a job template with a project configured for scm_update_on_launch.""" + objects = job_template_factory( + 'jt', + organization='org1', + project='proj', + inventory='inv', + credential='cred', + ) + p = objects.project + p.scm_update_on_launch = True + p.scm_update_cache_timeout = 0 + p.save(skip_update=True) + return objects + + +def _create_job_with_dependency(objects): + """Create a pending job and run DependencyManager to produce its project update dependency. + + Returns (job, project_update). + """ + j = create_job(objects.job_template, dependencies_processed=False) + with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): + DependencyManager().schedule() + assert j.dependent_jobs.count() == 1 + pu = j.dependent_jobs.first() + assert isinstance(pu.get_real_instance(), ProjectUpdate) + return j, pu + + +@pytest.mark.django_db +class TestCanceledDependencyFailsBlockedJob: + """When a dependency project update is canceled or failed, the task manager + should fail the blocked job via process_job_dep_failures.""" + + def test_canceled_dependency_fails_blocked_job(self, controlplane_instance_group, scm_on_launch_objects): + """A canceled dependency causes the blocked job to be failed with + a 'Previous Task Canceled' explanation.""" + j, pu = _create_job_with_dependency(scm_on_launch_objects) + + ProjectUpdate.objects.filter(pk=pu.pk).update(status='canceled', cancel_flag=True) + + with mock.patch("awx.main.scheduler.TaskManager.start_task"): + TaskManager().schedule() + + j.refresh_from_db() + assert j.status == 'failed' + assert 'Previous Task Canceled' in j.job_explanation + + def test_failed_dependency_fails_blocked_job(self, controlplane_instance_group, scm_on_launch_objects): + """A failed dependency causes the blocked job to be failed with + a 'Previous Task Failed' explanation.""" + j, pu = _create_job_with_dependency(scm_on_launch_objects) + + ProjectUpdate.objects.filter(pk=pu.pk).update(status='failed') + + with mock.patch("awx.main.scheduler.TaskManager.start_task"): + TaskManager().schedule() + + j.refresh_from_db() + assert j.status == 'failed' + assert 'Previous Task Failed' in j.job_explanation + + +@pytest.mark.django_db +class TestTaskManagerCancelsPendingJobsWithCancelFlag: + """When the task manager encounters pending jobs that have cancel_flag set, + it should transition them directly to canceled status.""" + + def test_pending_job_with_cancel_flag_is_canceled(self, controlplane_instance_group, job_template_factory): + """A pending job with cancel_flag=True is transitioned to canceled + by the task manager without being started.""" + objects = job_template_factory( + 'jt', + organization='org1', + project='proj', + inventory='inv', + credential='cred', + ) + j = create_job(objects.job_template) + j.cancel_flag = True + j.save(update_fields=['cancel_flag']) + + with mock.patch("awx.main.scheduler.TaskManager.start_task") as mock_start: + TaskManager().schedule() + + j.refresh_from_db() + assert j.status == 'canceled' + assert 'canceled before it started' in j.job_explanation + assert not mock_start.called + + def test_pending_job_without_cancel_flag_is_not_canceled(self, controlplane_instance_group, job_template_factory): + """A normal pending job without cancel_flag should not be canceled + by the task manager (sanity check).""" + objects = job_template_factory( + 'jt', + organization='org1', + project='proj', + inventory='inv', + credential='cred', + ) + j = create_job(objects.job_template) + + with mock.patch("awx.main.scheduler.TaskManager.start_task"): + TaskManager().schedule() + + j.refresh_from_db() + assert j.status != 'canceled' + + def test_multiple_pending_jobs_with_cancel_flag_bulk_canceled(self, controlplane_instance_group, job_template_factory): + """Multiple pending jobs with cancel_flag=True are all transitioned + to canceled in a single task manager cycle.""" + objects = job_template_factory( + 'jt', + organization='org1', + project='proj', + inventory='inv', + credential='cred', + ) + jt = objects.job_template + jt.allow_simultaneous = True + jt.save() + + jobs = [] + for _ in range(3): + j = create_job(jt) + j.cancel_flag = True + j.save(update_fields=['cancel_flag']) + jobs.append(j) + + with mock.patch("awx.main.scheduler.TaskManager.start_task") as mock_start: + TaskManager().schedule() + + for j in jobs: + j.refresh_from_db() + assert j.status == 'canceled', f"Job {j.id} should be canceled but is {j.status}" + assert 'canceled before it started' in j.job_explanation + assert not mock_start.called + + +@pytest.mark.django_db +class TestWorkflowCancelFinalizesWorkflow: + """When a workflow job is canceled, the WorkflowManager cancels spawned child + jobs (setting cancel_flag), the TaskManager transitions those pending jobs to + canceled, and a final WorkflowManager pass finalizes the workflow as canceled.""" + + def test_cancel_workflow_with_parallel_nodes(self, inventory, project, controlplane_instance_group): + """Create a workflow with parallel nodes, cancel it after one job is + running, and verify all jobs and the workflow reach canceled status.""" + jt = JobTemplate.objects.create(allow_simultaneous=False, inventory=inventory, project=project, playbook='helloworld.yml') + wfjt = WorkflowJobTemplate.objects.create(name='test-cancel-wf') + for _ in range(4): + wfjt.workflow_nodes.create(unified_job_template=jt) + + wj = wfjt.create_unified_job() + wj.signal_start() + + # TaskManager transitions workflow job to running via start_task + TaskManager().schedule() + wj.refresh_from_db() + assert wj.status == 'running' + + # WorkflowManager spawns jobs for all 4 nodes + WorkflowManager().schedule() + assert jt.jobs.count() == 4 + + # Simulate one job running (blocking the others via allow_simultaneous=False) + first_job = jt.jobs.order_by('created').first() + first_job.status = 'running' + first_job.celery_task_id = 'fake-task-id' + first_job.controller_node = 'test-node' + first_job.save(update_fields=['status', 'celery_task_id', 'controller_node']) + + # Cancel the workflow + wj.cancel_flag = True + wj.save(update_fields=['cancel_flag']) + + # WorkflowManager sees cancel_flag, calls cancel_node_jobs() which sets + # cancel_flag on all child jobs + with mock.patch('awx.main.models.unified_jobs.UnifiedJob.cancel_dispatcher_process'): + WorkflowManager().schedule() + + # The running job won't actually stop in tests (no dispatcher), simulate it + first_job.status = 'canceled' + first_job.save(update_fields=['status']) + + # TaskManager processes remaining pending jobs with cancel_flag set + with mock.patch("awx.main.scheduler.TaskManager.start_task") as mock_start: + DependencyManager().schedule() + TaskManager().schedule() + + for job in jt.jobs.all(): + job.refresh_from_db() + assert job.status == 'canceled', f"Job {job.id} should be canceled but is {job.status}" + assert not mock_start.called + + # Final WorkflowManager pass finalizes the workflow + WorkflowManager().schedule() + wj.refresh_from_db() + assert wj.status == 'canceled' + + def test_cancel_workflow_with_approval_node(self, controlplane_instance_group): + """Create a workflow with a pending approval node and a downstream job + node. Cancel the workflow and verify the approval is directly canceled + by the WorkflowManager (since approvals are excluded from TaskManager), + the downstream node is marked do_not_run, and the workflow finalizes.""" + approval_template = WorkflowApprovalTemplate.objects.create(name='test-approval', timeout=0) + wfjt = WorkflowJobTemplate.objects.create(name='test-cancel-approval-wf') + approval_node = wfjt.workflow_nodes.create(unified_job_template=approval_template) + + # Add a downstream node (just another approval to keep it simple) + downstream_template = WorkflowApprovalTemplate.objects.create(name='test-downstream', timeout=0) + downstream_node = wfjt.workflow_nodes.create(unified_job_template=downstream_template) + approval_node.success_nodes.add(downstream_node) + + wj = wfjt.create_unified_job() + wj.signal_start() + + # TaskManager transitions workflow to running + TaskManager().schedule() + wj.refresh_from_db() + assert wj.status == 'running' + + # WorkflowManager spawns the approval (root node only, downstream waits) + WorkflowManager().schedule() + assert WorkflowApproval.objects.filter(unified_job_node__workflow_job=wj).count() == 1 + + approval_job = WorkflowApproval.objects.get(unified_job_node__workflow_job=wj) + assert approval_job.status == 'pending' + + # Cancel the workflow + wj.cancel_flag = True + wj.save(update_fields=['cancel_flag']) + + # WorkflowManager should cancel the approval directly and mark + # the downstream node as do_not_run + WorkflowManager().schedule() + + approval_job.refresh_from_db() + assert approval_job.status == 'canceled', f"Approval should be canceled directly by WorkflowManager but is {approval_job.status}" + + # Downstream node should be marked do_not_run with no job spawned + downstream_wj_node = wj.workflow_nodes.get(unified_job_template=downstream_template) + assert downstream_wj_node.do_not_run is True + assert downstream_wj_node.job is None + + # Workflow should finalize as canceled in the same pass + wj.refresh_from_db() + assert wj.status == 'canceled' diff --git a/awx/main/tests/functional/task_management/test_rampart_groups.py b/awx/main/tests/functional/task_management/test_rampart_groups.py index 48ea9edb0805..f4bb81f405a6 100644 --- a/awx/main/tests/functional/task_management/test_rampart_groups.py +++ b/awx/main/tests/functional/task_management/test_rampart_groups.py @@ -21,13 +21,13 @@ def test_multi_group_basic_job_launch(instance_factory, controlplane_instance_gr j2 = create_job(objects2.job_template) with mock.patch('awx.main.models.Job.task_impact', new_callable=mock.PropertyMock) as mock_task_impact: mock_task_impact.return_value = 500 - with mocker.patch("awx.main.scheduler.TaskManager.start_task"): - TaskManager().schedule() - TaskManager.start_task.assert_has_calls([mock.call(j1, ig1, i1), mock.call(j2, ig2, i2)]) + mocker.patch("awx.main.scheduler.TaskManager.start_task") + TaskManager().schedule() + TaskManager.start_task.assert_has_calls([mock.call(j1, ig1, i1), mock.call(j2, ig2, i2)]) @pytest.mark.django_db -def test_multi_group_with_shared_dependency(instance_factory, controlplane_instance_group, mocker, instance_group_factory, job_template_factory): +def test_multi_group_with_shared_dependency(instance_factory, controlplane_instance_group, instance_group_factory, job_template_factory): i1 = instance_factory("i1") i2 = instance_factory("i2") ig1 = instance_group_factory("ig1", instances=[i1]) @@ -50,7 +50,7 @@ def test_multi_group_with_shared_dependency(instance_factory, controlplane_insta objects2 = job_template_factory('jt2', organization=objects1.organization, project=p, inventory='inv2', credential='cred2') objects2.job_template.instance_groups.add(ig2) j2 = create_job(objects2.job_template, dependencies_processed=False) - with mocker.patch("awx.main.scheduler.TaskManager.start_task"): + with mock.patch("awx.main.scheduler.TaskManager.start_task"): DependencyManager().schedule() TaskManager().schedule() pu = p.project_updates.first() @@ -73,10 +73,10 @@ def test_workflow_job_no_instancegroup(workflow_job_template_factory, controlpla wfj = wfjt.create_unified_job() wfj.status = "pending" wfj.save() - with mocker.patch("awx.main.scheduler.TaskManager.start_task"): - TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(wfj, None, None) - assert wfj.instance_group is None + mocker.patch("awx.main.scheduler.TaskManager.start_task") + TaskManager().schedule() + TaskManager.start_task.assert_called_once_with(wfj, None, None) + assert wfj.instance_group is None @pytest.mark.django_db diff --git a/awx/main/tests/functional/task_management/test_scheduler.py b/awx/main/tests/functional/task_management/test_scheduler.py index 42d144d5ccd3..7293873d7ccd 100644 --- a/awx/main/tests/functional/task_management/test_scheduler.py +++ b/awx/main/tests/functional/task_management/test_scheduler.py @@ -5,7 +5,7 @@ from awx.main.scheduler import TaskManager, DependencyManager, WorkflowManager from awx.main.utils import encrypt_field -from awx.main.models import WorkflowJobTemplate, JobTemplate, Job +from awx.main.models import WorkflowJobTemplate, JobTemplate, Job, Project, InventorySource, Inventory from awx.main.models.ha import Instance from . import create_job from django.conf import settings @@ -16,9 +16,9 @@ def test_single_job_scheduler_launch(hybrid_instance, controlplane_instance_grou instance = controlplane_instance_group.instances.all()[0] objects = job_template_factory('jt', organization='org1', project='proj', inventory='inv', credential='cred') j = create_job(objects.job_template) - with mocker.patch("awx.main.scheduler.TaskManager.start_task"): - TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(j, controlplane_instance_group, instance) + mocker.patch("awx.main.scheduler.TaskManager.start_task") + TaskManager().schedule() + TaskManager.start_task.assert_called_once_with(j, controlplane_instance_group, instance) @pytest.mark.django_db @@ -331,15 +331,13 @@ def test_single_job_dependencies_project_launch(controlplane_instance_group, job p.save(skip_update=True) with mock.patch("awx.main.scheduler.TaskManager.start_task"): dm = DependencyManager() - with mock.patch.object(DependencyManager, "create_project_update", wraps=dm.create_project_update) as mock_pu: - dm.schedule() - mock_pu.assert_called_once_with(j) - pu = [x for x in p.project_updates.all()] - assert len(pu) == 1 - TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(pu[0], controlplane_instance_group, instance) - pu[0].status = "successful" - pu[0].save() + dm.schedule() + pu = [x for x in p.project_updates.all()] + assert len(pu) == 1 + TaskManager().schedule() + TaskManager.start_task.assert_called_once_with(pu[0], controlplane_instance_group, instance) + pu[0].status = "successful" + pu[0].save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() TaskManager.start_task.assert_called_once_with(j, controlplane_instance_group, instance) @@ -359,22 +357,21 @@ def test_single_job_dependencies_inventory_update_launch(controlplane_instance_g i.inventory_sources.add(ii) with mock.patch("awx.main.scheduler.TaskManager.start_task"): dm = DependencyManager() - with mock.patch.object(DependencyManager, "create_inventory_update", wraps=dm.create_inventory_update) as mock_iu: - dm.schedule() - mock_iu.assert_called_once_with(j, ii) - iu = [x for x in ii.inventory_updates.all()] - assert len(iu) == 1 - TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(iu[0], controlplane_instance_group, instance) - iu[0].status = "successful" - iu[0].save() + dm.schedule() + assert ii.inventory_updates.count() == 1 + iu = [x for x in ii.inventory_updates.all()] + assert len(iu) == 1 + TaskManager().schedule() + TaskManager.start_task.assert_called_once_with(iu[0], controlplane_instance_group, instance) + iu[0].status = "successful" + iu[0].save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() TaskManager.start_task.assert_called_once_with(j, controlplane_instance_group, instance) @pytest.mark.django_db -def test_inventory_update_launches_project_update(controlplane_instance_group, scm_inventory_source): +def test_inventory_update_launches_project_update(scm_inventory_source): ii = scm_inventory_source project = scm_inventory_source.source_project project.scm_update_on_launch = True @@ -382,11 +379,51 @@ def test_inventory_update_launches_project_update(controlplane_instance_group, s iu = ii.create_inventory_update() iu.status = "pending" iu.save() + assert project.project_updates.count() == 0 with mock.patch("awx.main.scheduler.TaskManager.start_task"): dm = DependencyManager() - with mock.patch.object(DependencyManager, "create_project_update", wraps=dm.create_project_update) as mock_pu: - dm.schedule() - mock_pu.assert_called_with(iu, project_id=project.id) + dm.schedule() + assert project.project_updates.count() == 1 + + +@pytest.mark.django_db +def test_dependency_isolation(organization): + """Spawning both a job project update dependency, and an inventory update project dependency + + this should keep dependencies isolated""" + with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): + updating_projects = [ + Project.objects.create(name=f'iso-proj{i}', organization=organization, scm_url='https://foo.invalid', scm_type='git', scm_update_on_launch=True) + for i in range(2) + ] + + inv_src = InventorySource.objects.create( + name='iso-inv', + organization=organization, + source_project=updating_projects[0], + source='scm', + inventory=Inventory.objects.create(name='for-inv-src', organization=organization), + ) + + inv_update = inv_src.create_unified_job() + inv_update.signal_start() + assert not inv_update.dependent_jobs.exists() + + jt = JobTemplate.objects.create( + project=updating_projects[1], + inventory=Inventory.objects.create(name='one-off', organization=organization), # non-updating inventory source + ) + job = jt.create_unified_job() + job.signal_start() + assert not job.dependent_jobs.exists() + + dm = DependencyManager() + dm.schedule() + + # in a single run, the completely unrelated inventory and jobs are linked to their own dependencies + assert (inv_update.dependent_jobs.count(), job.dependent_jobs.count()) == (1, 1) + assert inv_update.dependent_jobs.first().project == updating_projects[0] + assert job.dependent_jobs.first().project == updating_projects[1] @pytest.mark.django_db @@ -407,9 +444,8 @@ def test_job_dependency_with_already_updated(controlplane_instance_group, job_te j.save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): dm = DependencyManager() - with mock.patch.object(DependencyManager, "create_inventory_update", wraps=dm.create_inventory_update) as mock_iu: - dm.schedule() - mock_iu.assert_not_called() + dm.schedule() + assert ii.inventory_updates.count() == 0 with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() TaskManager.start_task.assert_called_once_with(j, controlplane_instance_group, instance) @@ -442,7 +478,9 @@ def test_shared_dependencies_launch(controlplane_instance_group, job_template_fa TaskManager().schedule() pu = p.project_updates.first() iu = ii.inventory_updates.first() - TaskManager.start_task.assert_has_calls([mock.call(iu, controlplane_instance_group, instance), mock.call(pu, controlplane_instance_group, instance)]) + TaskManager.start_task.assert_has_calls( + [mock.call(iu, controlplane_instance_group, instance), mock.call(pu, controlplane_instance_group, instance)], any_order=True + ) pu.status = "successful" pu.finished = pu.created + timedelta(seconds=1) pu.save() @@ -451,7 +489,9 @@ def test_shared_dependencies_launch(controlplane_instance_group, job_template_fa iu.save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() - TaskManager.start_task.assert_has_calls([mock.call(j1, controlplane_instance_group, instance), mock.call(j2, controlplane_instance_group, instance)]) + TaskManager.start_task.assert_has_calls( + [mock.call(j1, controlplane_instance_group, instance), mock.call(j2, controlplane_instance_group, instance)], any_order=True + ) pu = [x for x in p.project_updates.all()] iu = [x for x in ii.inventory_updates.all()] assert len(pu) == 1 diff --git a/awx/main/tests/functional/tasks/test_fact_cache.py b/awx/main/tests/functional/tasks/test_fact_cache.py new file mode 100644 index 000000000000..07e6a325ecc6 --- /dev/null +++ b/awx/main/tests/functional/tasks/test_fact_cache.py @@ -0,0 +1,223 @@ +"""Functional tests for start_fact_cache / finish_fact_cache. + +These tests use real database objects (via pytest-django) and real files +on disk, but do not launch jobs or subprocesses. Fact files are written +by start_fact_cache and then manipulated to simulate ansible output +before calling finish_fact_cache. + +Generated by Claude Opus 4.6 (claude-opus-4-6). +""" + +import json +import os +import time +from datetime import timedelta + +import pytest + +from django.utils.timezone import now + +from awx.main.models import Host, Inventory +from awx.main.tasks.facts import start_fact_cache, finish_fact_cache + + +@pytest.fixture +def artifacts_dir(tmp_path): + d = tmp_path / 'artifacts' + d.mkdir() + return str(d) + + +@pytest.mark.django_db +class TestFinishFactCacheScoping: + """finish_fact_cache must only update hosts matched by the provided queryset.""" + + def test_same_hostname_different_inventories(self, organization, artifacts_dir): + """Two inventories share a hostname; only the targeted one should be updated. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + """ + inv1 = Inventory.objects.create(organization=organization, name='scope-inv1') + inv2 = Inventory.objects.create(organization=organization, name='scope-inv2') + + host1 = inv1.hosts.create(name='shared') + host2 = inv2.hosts.create(name='shared') + + # Give both hosts initial facts + for h in (host1, host2): + h.ansible_facts = {'original': True} + h.ansible_facts_modified = now() + h.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + # start_fact_cache writes reference files for inv1's hosts + start_fact_cache(inv1.hosts.all(), artifacts_dir=artifacts_dir, timeout=0, inventory_id=inv1.id) + + # Simulate ansible writing new facts for 'shared' + fact_file = os.path.join(artifacts_dir, 'fact_cache', 'shared') + future = time.time() + 60 + with open(fact_file, 'w') as f: + json.dump({'updated': True}, f) + os.utime(fact_file, (future, future)) + + # finish with inv1's hosts as the queryset + finish_fact_cache(inv1.hosts, artifacts_dir=artifacts_dir, inventory_id=inv1.id) + + host1.refresh_from_db() + host2.refresh_from_db() + + assert host1.ansible_facts == {'updated': True} + assert host2.ansible_facts == {'original': True}, 'Host in a different inventory was modified despite not being in the queryset' + + +@pytest.mark.django_db +class TestFinishFactCacheConcurrentProtection: + """finish_fact_cache must not clear facts that a concurrent job updated.""" + + def test_no_clear_when_no_file_was_written(self, organization, artifacts_dir): + """Host with no prior facts should not have facts cleared when file is missing. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + + start_fact_cache records hosts_cached[host] = False for hosts with no + prior facts (no file written). finish_fact_cache should skip the clear + for these hosts because the missing file is expected, not a clear signal. + """ + inv = Inventory.objects.create(organization=organization, name='concurrent-inv') + host = inv.hosts.create(name='target') + + job_created = now() - timedelta(minutes=5) + + # start_fact_cache records host with False (no facts → no file written) + start_fact_cache(inv.hosts.all(), artifacts_dir=artifacts_dir, timeout=0, inventory_id=inv.id) + + # Simulate a concurrent job updating this host's facts AFTER our job was created + host.ansible_facts = {'from_concurrent_job': True} + host.ansible_facts_modified = now() + host.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + # The fact file is missing because start_fact_cache never wrote one. + # finish_fact_cache should skip this host entirely. + finish_fact_cache( + inv.hosts, + artifacts_dir=artifacts_dir, + inventory_id=inv.id, + job_created=job_created, + ) + + host.refresh_from_db() + assert host.ansible_facts == {'from_concurrent_job': True}, 'Facts were cleared for a host that never had a fact file written' + + def test_skip_clear_when_facts_modified_after_job_created(self, organization, artifacts_dir): + """If a file was written and then deleted, but facts were concurrently updated, skip clear. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + """ + inv = Inventory.objects.create(organization=organization, name='concurrent-written-inv') + host = inv.hosts.create(name='target') + + old_time = now() - timedelta(hours=1) + host.ansible_facts = {'original': True} + host.ansible_facts_modified = old_time + host.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + job_created = now() - timedelta(minutes=5) + + # start_fact_cache writes a file (host has facts → True in map) + start_fact_cache(inv.hosts.all(), artifacts_dir=artifacts_dir, timeout=0, inventory_id=inv.id) + + # Remove the fact file (ansible didn't target this host via --limit) + os.remove(os.path.join(artifacts_dir, 'fact_cache', host.name)) + + # Simulate a concurrent job updating this host's facts AFTER our job was created + host.ansible_facts = {'from_concurrent_job': True} + host.ansible_facts_modified = now() + host.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + finish_fact_cache( + inv.hosts, + artifacts_dir=artifacts_dir, + inventory_id=inv.id, + job_created=job_created, + ) + + host.refresh_from_db() + assert host.ansible_facts == {'from_concurrent_job': True}, 'Facts set by a concurrent job were cleared despite ansible_facts_modified > job_created' + + def test_clear_when_facts_predate_job(self, organization, artifacts_dir): + """If facts predate the job, a missing file should still clear them. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + """ + inv = Inventory.objects.create(organization=organization, name='clear-inv') + host = inv.hosts.create(name='stale') + + old_time = now() - timedelta(hours=1) + host.ansible_facts = {'stale': True} + host.ansible_facts_modified = old_time + host.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + job_created = now() - timedelta(minutes=5) + + start_fact_cache(inv.hosts.all(), artifacts_dir=artifacts_dir, timeout=0, inventory_id=inv.id) + + # Remove the fact file to simulate ansible's clear_facts + os.remove(os.path.join(artifacts_dir, 'fact_cache', host.name)) + + finish_fact_cache( + inv.hosts, + artifacts_dir=artifacts_dir, + inventory_id=inv.id, + job_created=job_created, + ) + + host.refresh_from_db() + assert host.ansible_facts == {}, 'Stale facts should have been cleared when the fact file is missing ' 'and ansible_facts_modified predates job_created' + + +@pytest.mark.django_db +class TestConstructedInventoryFactCache: + """finish_fact_cache with a constructed inventory queryset must target source hosts.""" + + def test_facts_resolve_to_source_host(self, organization, artifacts_dir): + """Facts must be written to the source host, not the constructed copy. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + """ + from django.db.models.functions import Cast + + inv_input = Inventory.objects.create(organization=organization, name='ci-input') + source_host = inv_input.hosts.create(name='webserver') + + inv_constructed = Inventory.objects.create(organization=organization, name='ci-constructed', kind='constructed') + inv_constructed.input_inventories.add(inv_input) + constructed_host = Host.objects.create( + inventory=inv_constructed, + name='webserver', + instance_id=str(source_host.id), + ) + + # Build the same queryset that get_hosts_for_fact_cache uses + id_field = Host._meta.get_field('id') + source_qs = Host.objects.filter(id__in=inv_constructed.hosts.exclude(instance_id='').values_list(Cast('instance_id', output_field=id_field))) + + # Give the source host initial facts so start_fact_cache writes a file + source_host.ansible_facts = {'role': 'web'} + source_host.ansible_facts_modified = now() + source_host.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + start_fact_cache(source_qs, artifacts_dir=artifacts_dir, timeout=0, inventory_id=inv_constructed.id) + + # Simulate ansible writing updated facts + fact_file = os.path.join(artifacts_dir, 'fact_cache', 'webserver') + future = time.time() + 60 + with open(fact_file, 'w') as f: + json.dump({'role': 'web', 'deployed': True}, f) + os.utime(fact_file, (future, future)) + + finish_fact_cache(source_qs, artifacts_dir=artifacts_dir, inventory_id=inv_constructed.id) + + source_host.refresh_from_db() + constructed_host.refresh_from_db() + + assert source_host.ansible_facts == {'role': 'web', 'deployed': True} + assert not constructed_host.ansible_facts, f'Facts were stored on the constructed host: {constructed_host.ansible_facts!r}' diff --git a/awx/main/tests/functional/tasks/test_host_indirect.py b/awx/main/tests/functional/tasks/test_host_indirect.py new file mode 100644 index 000000000000..cfa98d2391cc --- /dev/null +++ b/awx/main/tests/functional/tasks/test_host_indirect.py @@ -0,0 +1,366 @@ +import yaml +from functools import reduce +from unittest import mock + +import pytest + +from django.utils.timezone import now, timedelta + +from awx.main.tasks.host_indirect import ( + build_indirect_host_data, + fetch_job_event_query, + save_indirect_host_entries, + cleanup_and_save_indirect_host_entries_fallback, +) +from awx.main.models.event_query import EventQuery +from awx.main.models.indirect_managed_node_audit import IndirectManagedNodeAudit + +"""These are unit tests, similar to test_indirect_host_counting in the live tests""" + + +TEST_JQ = "{name: .name, canonical_facts: {host_name: .direct_host_name}, facts: {another_host_name: .direct_host_name}}" + + +class Query(dict): + def __init__(self, resolved_action: str, query_jq: dict): + self._resolved_action = resolved_action.split('.') + self._collection_ns, self._collection_name, self._module_name = self._resolved_action + + super().__init__({self.resolve_key: {'query': query_jq}}) + + def get_fqcn(self): + return f'{self._collection_ns}.{self._collection_name}' + + @property + def resolve_value(self): + return self[self.resolve_key] + + @property + def resolve_key(self): + return f'{self.get_fqcn()}.{self._module_name}' + + def resolve(self, module_name=None): + return {f'{self.get_fqcn()}.{module_name or self._module_name}': self.resolve_value} + + def create_event_query(self, module_name=None): + if (module_name := module_name or self._module_name) == '*': + raise ValueError('Invalid module name *') + return self.create_event_queries([module_name]) + + def create_event_queries(self, module_names): + queries = {} + for name in module_names: + queries |= self.resolve(name) + return EventQuery.objects.create( + fqcn=self.get_fqcn(), + collection_version='1.0.1', + event_query=yaml.dump(queries, default_flow_style=False), + ) + + def create_registered_event(self, job, module_name): + job.job_events.create(event_data={'resolved_action': f'{self.get_fqcn()}.{module_name}', 'res': {'direct_host_name': 'foo_host', 'name': 'vm-foo'}}) + + +@pytest.fixture +def bare_job(job_factory): + job = job_factory() + job.installed_collections = {'demo.query': {'version': '1.0.1'}, 'demo2.query': {'version': '1.0.1'}} + job.event_queries_processed = False + job.save(update_fields=['installed_collections', 'event_queries_processed']) + return job + + +def create_registered_event(job, task_name='demo.query.example'): + return job.job_events.create(event_data={'resolved_action': task_name, 'res': {'direct_host_name': 'foo_host', 'name': 'vm-foo'}}) + + +@pytest.fixture +def job_with_counted_event(bare_job): + create_registered_event(bare_job) + return bare_job + + +def create_audit_record(name, job, organization, created=now()): + record = IndirectManagedNodeAudit.objects.create(name=name, job=job, organization=organization) + record.created = created + record.save() + return record + + +@pytest.fixture +def event_query(): + "This is ordinarily created by the artifacts callback" + return Query('demo.query.example', TEST_JQ).create_event_query() + + +@pytest.fixture +def old_audit_record(bare_job, organization): + created_at = now() - timedelta(days=10) + return create_audit_record(name="old_job", job=bare_job, organization=organization, created=created_at) + + +@pytest.fixture +def new_audit_record(bare_job, organization): + return IndirectManagedNodeAudit.objects.create(name="new_job", job=bare_job, organization=organization) + + +# ---- end fixtures ---- + + +@pytest.mark.django_db +@pytest.mark.parametrize( + 'queries,expected_matches', + ( + pytest.param( + [], + 0, + id='no_results', + ), + pytest.param( + [Query('demo.query.example', TEST_JQ)], + 1, + id='fully_qualified', + ), + pytest.param( + [Query('demo.query.*', TEST_JQ)], + 1, + id='wildcard', + ), + pytest.param( + [ + Query('demo.query.*', TEST_JQ), + Query('demo.query.example', TEST_JQ), + ], + 1, + id='wildcard_and_fully_qualified', + ), + pytest.param( + [ + Query('demo.query.*', TEST_JQ), + Query('demo.query.example', {}), + ], + 0, + id='wildcard_and_fully_qualified', + ), + pytest.param( + [ + Query('demo.query.example', {}), + Query('demo.query.*', TEST_JQ), + ], + 0, + id='ordering_should_not_matter', + ), + ), +) +def test_build_indirect_host_data(job_with_counted_event, queries: Query, expected_matches: int): + data = build_indirect_host_data(job_with_counted_event, {k: v for d in queries for k, v in d.items()}) + assert len(data) == expected_matches + + +@mock.patch('awx.main.tasks.host_indirect.logger.debug') +@pytest.mark.django_db +@pytest.mark.parametrize( + 'task_name', + ( + pytest.param( + 'demo.query', + id='no_results', + ), + pytest.param( + 'demo', + id='no_results', + ), + pytest.param( + 'a.b.c.d', + id='no_results', + ), + ), +) +def test_build_indirect_host_data_malformed_module_name(mock_logger_debug, bare_job, task_name: str): + create_registered_event(bare_job, task_name) + assert build_indirect_host_data(bare_job, Query('demo.query.example', TEST_JQ)) == [] + mock_logger_debug.assert_called_once_with(f"Malformed invocation module name '{task_name}'. Expected to be of the form 'a.b.c'") + + +@mock.patch('awx.main.tasks.host_indirect.logger.info') +@pytest.mark.django_db +@pytest.mark.parametrize( + 'query', + ( + pytest.param( + 'demo.query', + id='no_results', + ), + pytest.param( + 'demo', + id='no_results', + ), + pytest.param( + 'a.b.c.d', + id='no_results', + ), + ), +) +def test_build_indirect_host_data_malformed_query(mock_logger_info, job_with_counted_event, query: str): + assert build_indirect_host_data(job_with_counted_event, {query: {'query': TEST_JQ}}) == [] + mock_logger_info.assert_called_once_with(f"Skiping malformed query '{query}'. Expected to be of the form 'a.b.c'") + + +@pytest.mark.django_db +@pytest.mark.parametrize( + 'query', + ( + pytest.param( + Query('demo.query.example', TEST_JQ), + id='fully_qualified', + ), + pytest.param( + Query('demo.query.*', TEST_JQ), + id='wildcard', + ), + ), +) +def test_fetch_job_event_query(bare_job, query: Query): + query.create_event_query(module_name='example') + assert fetch_job_event_query(bare_job) == query.resolve('example') + + +@pytest.mark.django_db +@pytest.mark.parametrize( + 'queries', + ( + [ + Query('demo.query.example', TEST_JQ), + Query('demo2.query.example', TEST_JQ), + ], + [ + Query('demo.query.*', TEST_JQ), + Query('demo2.query.example', TEST_JQ), + ], + ), +) +def test_fetch_multiple_job_event_query(bare_job, queries: list[Query]): + for q in queries: + q.create_event_query(module_name='example') + assert fetch_job_event_query(bare_job) == reduce(lambda acc, q: acc | q.resolve('example'), queries, {}) + + +@pytest.mark.django_db +@pytest.mark.parametrize( + ('state',), + ( + pytest.param( + [ + ( + Query('demo.query.example', TEST_JQ), + ['example'], + ), + ], + id='fully_qualified', + ), + pytest.param( + [ + ( + Query('demo.query.example', TEST_JQ), + ['example'] * 3, + ), + ], + id='multiple_events_same_module_same_host', + ), + pytest.param( + [ + ( + Query('demo.query.example', TEST_JQ), + ['example'], + ), + ( + Query('demo2.query.example', TEST_JQ), + ['example'], + ), + ], + id='multiple_modules', + ), + pytest.param( + [ + ( + Query('demo.query.*', TEST_JQ), + ['example', 'example2'], + ), + ], + id='multiple_modules_same_collection', + ), + ), +) +def test_save_indirect_host_entries(bare_job, state): + all_task_names = [] + for entry in state: + query, module_names = entry + all_task_names.extend([f'{query.get_fqcn()}.{module_name}' for module_name in module_names]) + query.create_event_queries(module_names) + [query.create_registered_event(bare_job, n) for n in module_names] + + save_indirect_host_entries(bare_job.id) + bare_job.refresh_from_db() + + assert bare_job.event_queries_processed is True + + assert IndirectManagedNodeAudit.objects.filter(job=bare_job).count() == 1 + host_audit = IndirectManagedNodeAudit.objects.filter(job=bare_job).first() + + assert host_audit.count == len(all_task_names) + assert host_audit.canonical_facts == {'host_name': 'foo_host'} + assert host_audit.facts == {'another_host_name': 'foo_host'} + assert host_audit.organization == bare_job.organization + assert host_audit.name == 'vm-foo' + assert set(host_audit.events) == set(all_task_names) + + +@pytest.mark.django_db +def test_events_not_fully_processed_no_op(bare_job): + # I have a job that produced 12 events, but those are not saved + bare_job.emitted_events = 12 + bare_job.finished = now() + bare_job.save(update_fields=['emitted_events', 'finished']) + + # Running the normal post-run task will do nothing at this point + assert bare_job.event_queries_processed is False + with mock.patch('time.sleep'): # for test speedup + save_indirect_host_entries(bare_job.id) + bare_job.refresh_from_db() + assert bare_job.event_queries_processed is False + + # Right away, the fallback processing will not run either + cleanup_and_save_indirect_host_entries_fallback() + bare_job.refresh_from_db() + assert bare_job.event_queries_processed is False + + # After 3 hours have passed... + bare_job.finished = now() - timedelta(hours=3) + + # Create the expected job events + for _ in range(12): + create_registered_event(bare_job) + + bare_job.save(update_fields=['finished']) + + # The fallback task will now process indirect host query data for this job + cleanup_and_save_indirect_host_entries_fallback() + + # Test code to process anyway, events collected or not + save_indirect_host_entries(bare_job.id, wait_for_events=False) + bare_job.refresh_from_db() + assert bare_job.event_queries_processed is True + + +@pytest.mark.django_db +def test_job_id_does_not_exist(): + save_indirect_host_entries(10000001) + + +@pytest.mark.django_db +def test_cleanup_old_audit_records(old_audit_record, new_audit_record): + count_before_cleanup = IndirectManagedNodeAudit.objects.count() + assert count_before_cleanup == 2 + cleanup_and_save_indirect_host_entries_fallback() + count_after_cleanup = IndirectManagedNodeAudit.objects.count() + assert count_after_cleanup == 1 diff --git a/awx/main/tests/functional/tasks/test_tasks_jobs.py b/awx/main/tests/functional/tasks/test_tasks_jobs.py new file mode 100644 index 000000000000..ffd5fce4e826 --- /dev/null +++ b/awx/main/tests/functional/tasks/test_tasks_jobs.py @@ -0,0 +1,58 @@ +import pytest + +from awx.main.tasks.jobs import RunJob +from awx.main.models import Job + + +@pytest.mark.django_db +def test_does_not_run_reaped_job(mocker, mock_me): + job = Job.objects.create(status='failed', job_explanation='This job has been reaped.') + mock_run = mocker.patch('awx.main.tasks.jobs.ansible_runner.interface.run') + try: + RunJob().run(job.id) + except Exception: + pass + job.refresh_from_db() + assert job.status == 'failed' + mock_run.assert_not_called() + + +@pytest.mark.django_db +def test_cancel_flag_on_start(jt_linked, caplog): + job = jt_linked.create_unified_job() + job.status = 'waiting' + job.cancel_flag = True + job.save() + + task = RunJob() + task.run(job.id) + + job = Job.objects.get(id=job.id) + assert job.status == 'canceled' + + +@pytest.mark.django_db +def test_runjob_run_can_accept_waiting_status(jt_linked, mocker): + """Test that RunJob.run() can accept a job in 'waiting' status and transition it to 'running' + before the pre_run_hook is called""" + job = jt_linked.create_unified_job() + job.status = 'waiting' + job.save() + + status_at_pre_run = None + + def capture_status(instance, private_data_dir): + nonlocal status_at_pre_run + instance.refresh_from_db() + status_at_pre_run = instance.status + + mock_pre_run = mocker.patch.object(RunJob, 'pre_run_hook', side_effect=capture_status) + + task = RunJob() + try: + task.run(job.id) + except Exception: + pass + + mock_pre_run.assert_called_once() + assert status_at_pre_run == 'running' diff --git a/awx/main/tests/functional/tasks/test_tasks_system.py b/awx/main/tests/functional/tasks/test_tasks_system.py new file mode 100644 index 000000000000..96ce7aa41347 --- /dev/null +++ b/awx/main/tests/functional/tasks/test_tasks_system.py @@ -0,0 +1,166 @@ +import copy +import json +import logging +import os +import tempfile +import shutil +from unittest import mock + +import pytest + +from awx.main.tasks.system import CleanupImagesAndFiles, execution_node_health_check, inspect_established_receptor_connections, clear_setting_cache +from awx.main.management.commands.dispatcherd import Command +from awx.main.models import Instance, Job, ReceptorAddress, InstanceLink + + +@pytest.mark.django_db +class TestLinkState: + @pytest.fixture(autouse=True) + def configure_settings(self, settings): + settings.IS_K8S = True + + def test_inspect_established_receptor_connections(self): + ''' + Change link state from ADDING to ESTABLISHED + if the receptor status KnownConnectionCosts field + has an entry for the source and target node. + ''' + hop1 = Instance.objects.create(hostname='hop1') + hop2 = Instance.objects.create(hostname='hop2') + hop2addr = ReceptorAddress.objects.create(instance=hop2, address='hop2', port=5678) + InstanceLink.objects.create(source=hop1, target=hop2addr, link_state=InstanceLink.States.ADDING) + + # calling with empty KnownConnectionCosts should not change the link state + inspect_established_receptor_connections({"KnownConnectionCosts": {}}) + assert InstanceLink.objects.get(source=hop1, target=hop2addr).link_state == InstanceLink.States.ADDING + + mesh_state = {"KnownConnectionCosts": {"hop1": {"hop2": 1}}} + inspect_established_receptor_connections(mesh_state) + assert InstanceLink.objects.get(source=hop1, target=hop2addr).link_state == InstanceLink.States.ESTABLISHED + + +@pytest.fixture +def job_folder_factory(request): + def _rf(job_id='1234'): + pdd_path = tempfile.mkdtemp(prefix=f'awx_{job_id}_') + + def test_folder_cleanup(): + if os.path.exists(pdd_path): + shutil.rmtree(pdd_path) + + request.addfinalizer(test_folder_cleanup) + + return pdd_path + + return _rf + + +@pytest.fixture +def mock_job_folder(job_folder_factory): + return job_folder_factory() + + +@pytest.mark.django_db +@pytest.mark.parametrize('node_type', ('control. hybrid')) +def test_no_worker_info_on_AWX_nodes(node_type): + hostname = 'us-south-3-compute.invalid' + Instance.objects.create(hostname=hostname, node_type=node_type) + assert execution_node_health_check(hostname) is None + + +@pytest.mark.django_db +def test_folder_cleanup_stale_file(mock_job_folder, mock_me): + CleanupImagesAndFiles.run() + assert os.path.exists(mock_job_folder) # grace period should protect folder from deletion + + CleanupImagesAndFiles.run(grace_period=0) + assert not os.path.exists(mock_job_folder) # should be deleted + + +@pytest.mark.django_db +def test_folder_cleanup_running_job(mock_job_folder, me_inst): + job = Job.objects.create(id=1234, controller_node=me_inst.hostname, status='running') + CleanupImagesAndFiles.run(grace_period=0) + assert os.path.exists(mock_job_folder) # running job should prevent folder from getting deleted + + job.status = 'failed' + job.save(update_fields=['status']) + CleanupImagesAndFiles.run(grace_period=0) + assert not os.path.exists(mock_job_folder) # job is finished and no grace period, should delete + + +@pytest.mark.django_db +def test_folder_cleanup_multiple_running_jobs(job_folder_factory, me_inst): + jobs = [] + dirs = [] + num_jobs = 3 + + for i in range(num_jobs): + job = Job.objects.create(controller_node=me_inst.hostname, status='running') + dirs.append(job_folder_factory(job.id)) + jobs.append(job) + + CleanupImagesAndFiles.run(grace_period=0) + + assert [os.path.exists(d) for d in dirs] == [True for i in range(num_jobs)] + + +@pytest.mark.django_db +def test_clear_setting_cache_log_level_branch(settings): + settings.LOG_AGGREGATOR_LEVEL = 'DEBUG' + settings.CLUSTER_HOST_ID = 'control-node' + published_messages = [] + + class DummyBroker: + def publish_message(self, channel, message): + published_messages.append((channel, message)) + + def close(self): + pass + + dummy_broker = DummyBroker() + + with mock.patch('dispatcherd.control.get_broker', return_value=dummy_broker) as mock_get_broker: + clear_setting_cache(['LOG_AGGREGATOR_LEVEL']) + + mock_get_broker.assert_called_once() + assert published_messages, 'control command was not sent through the broker' + queue, payload = published_messages[-1] + assert queue == 'control-node' + body = json.loads(payload) + assert body['control'] == 'set_log_level' + assert body['control_data'] == {'level': 'DEBUG'} + + +@pytest.mark.django_db +def test_configure_dispatcher_logging_updates_level(settings): + original_logging_settings = copy.deepcopy(settings.LOGGING) + settings.LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'dynamic_level_filter': { + '()': 'logging.Filter', + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'filters': ['dynamic_level_filter'], + 'stream': 'ext://sys.stdout', + } + }, + 'loggers': { + 'dispatcherd': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + } + }, + } + settings.LOG_AGGREGATOR_LEVEL = 'WARNING' + + Command().configure_dispatcher_logging() + + assert logging.getLogger('dispatcherd').level == logging.WARNING + settings.LOGGING = original_logging_settings diff --git a/awx/main/tests/functional/test_apps.py b/awx/main/tests/functional/test_apps.py new file mode 100644 index 000000000000..a52d4aa723ed --- /dev/null +++ b/awx/main/tests/functional/test_apps.py @@ -0,0 +1,26 @@ +import pytest + +from django.apps import apps + + +@pytest.fixture +def mock_setup_tower_managed_defaults(mocker): + return mocker.patch('awx.main.models.credential.CredentialType.setup_tower_managed_defaults') + + +@pytest.mark.django_db +def test_load_credential_types_feature_migrations_ran(mocker, mock_setup_tower_managed_defaults): + mocker.patch('awx.main.apps.is_database_synchronized', return_value=True) + + apps.get_app_config('main')._load_credential_types_feature() + + mock_setup_tower_managed_defaults.assert_called_once() + + +@pytest.mark.django_db +def test_load_credential_types_feature_migrations_not_ran(mocker, mock_setup_tower_managed_defaults): + mocker.patch('awx.main.apps.is_database_synchronized', return_value=False) + + apps.get_app_config('main')._load_credential_types_feature() + + mock_setup_tower_managed_defaults.assert_not_called() diff --git a/awx/main/tests/functional/test_bulk.py b/awx/main/tests/functional/test_bulk.py new file mode 100644 index 000000000000..6b166cdf2bff --- /dev/null +++ b/awx/main/tests/functional/test_bulk.py @@ -0,0 +1,447 @@ +import pytest + +from uuid import uuid4 + +from awx.api.versioning import reverse + +from awx.main.models.jobs import JobTemplate +from awx.main.models import Organization, Inventory, WorkflowJob, ExecutionEnvironment, Host +from awx.main.scheduler import TaskManager + + +@pytest.mark.django_db +@pytest.mark.parametrize('num_hosts, num_queries', [(1, 15), (10, 15)]) +def test_bulk_host_create_num_queries(organization, inventory, post, get, user, num_hosts, num_queries, django_assert_max_num_queries): + ''' + If I am a... + org admin + inventory admin at org level + admin of a particular inventory + superuser + + Bulk Host create should take under a certain number of queries + ''' + inventory.organization = organization + inventory_admin = user('inventory_admin', False) + org_admin = user('org_admin', False) + org_inv_admin = user('org_admin', False) + superuser = user('admin', True) + for u in [org_admin, org_inv_admin, inventory_admin]: + organization.member_role.members.add(u) + organization.admin_role.members.add(org_admin) + organization.inventory_admin_role.members.add(org_inv_admin) + inventory.admin_role.members.add(inventory_admin) + + for u in [org_admin, inventory_admin, org_inv_admin, superuser]: + hosts = [{'name': uuid4()} for i in range(num_hosts)] + with django_assert_max_num_queries(num_queries): + bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, u, expect=201).data + assert len(bulk_host_create_response['hosts']) == len(hosts), f"unexpected number of hosts created for user {u}" + + +@pytest.mark.django_db +def test_bulk_host_create_rbac(organization, inventory, post, get, user): + ''' + If I am a... + org admin + inventory admin at org level + admin of a particular invenotry + ... I can bulk add hosts + + Everyone else cannot + ''' + inventory.organization = organization + inventory_admin = user('inventory_admin', False) + org_admin = user('org_admin', False) + org_inv_admin = user('org_admin', False) + auditor = user('auditor', False) + member = user('member', False) + use_inv_member = user('member', False) + for u in [org_admin, org_inv_admin, auditor, member, inventory_admin, use_inv_member]: + organization.member_role.members.add(u) + organization.admin_role.members.add(org_admin) + organization.inventory_admin_role.members.add(org_inv_admin) + inventory.admin_role.members.add(inventory_admin) + inventory.use_role.members.add(use_inv_member) + organization.auditor_role.members.add(auditor) + + for indx, u in enumerate([org_admin, inventory_admin, org_inv_admin]): + bulk_host_create_response = post( + reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': [{'name': f'foobar-{indx}'}]}, u, expect=201 + ).data + assert len(bulk_host_create_response['hosts']) == 1, f"unexpected number of hosts created for user {u}" + assert Host.objects.filter(inventory__id=inventory.id)[0].name == 'foobar-0' + + for indx, u in enumerate([member, auditor, use_inv_member]): + bulk_host_create_response = post( + reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': [{'name': f'foobar2-{indx}'}]}, u, expect=400 + ).data + assert bulk_host_create_response['__all__'][0] == f'Inventory with id {inventory.id} not found or lack permissions to add hosts.' + + +@pytest.mark.django_db +@pytest.mark.parametrize('num_jobs, num_queries', [(1, 25), (10, 25)]) +def test_bulk_job_launch_queries(job_template, organization, inventory, project, post, get, user, num_jobs, num_queries, django_assert_max_num_queries): + ''' + if I have access to the unified job template + ... I can launch the bulk job + ... and the number of queries should NOT scale with the number of jobs + ''' + normal_user = user('normal_user', False) + org_admin = user('org_admin', False) + jt = JobTemplate.objects.create(name='my-jt', ask_inventory_on_launch=True, project=project, playbook='helloworld.yml') + organization.member_role.members.add(normal_user) + organization.admin_role.members.add(org_admin) + jt.execute_role.members.add(normal_user) + inventory.use_role.members.add(normal_user) + jt.save() + inventory.save() + jobs = [{'unified_job_template': jt.id, 'inventory': inventory.id} for _ in range(num_jobs)] + + # This is not working, we need to figure that out if we want to include tests for more jobs + # with mock.patch('awx.api.serializers.settings.BULK_JOB_MAX_LAUNCH', num_jobs + 1): + with django_assert_max_num_queries(num_queries): + bulk_job_launch_response = post(reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': jobs}, normal_user, expect=201).data + + # Run task manager so the workflow job nodes actually spawn + TaskManager().schedule() + + for u in (org_admin, normal_user): + bulk_job = get(bulk_job_launch_response['url'], u, expect=200).data + assert organization.id == bulk_job['summary_fields']['organization']['id'] + resp = get(bulk_job_launch_response['related']['workflow_nodes'], u) + assert resp.data['count'] == num_jobs + for item in resp.data['results']: + assert item["unified_job_template"] == jt.id + assert item["inventory"] == inventory.id + + +@pytest.mark.django_db +def test_bulk_job_launch_no_access_to_job_template(job_template, organization, inventory, project, credential, post, get, user): + ''' + if I don't have access to the unified job templare + ... I can't launch the bulk job + ''' + normal_user = user('normal_user', False) + jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') + jt.save() + organization.member_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}]}, normal_user, expect=400 + ).data + assert bulk_job_launch_response['__all__'][0] == f'Job Templates {{{jt.id}}} not found or you don\'t have permissions to access it' + + +@pytest.mark.django_db +def test_bulk_job_launch_no_org_assigned(job_template, organization, inventory, project, credential, post, get, user): + ''' + if I am not part of any organization... + ... I can't launch the bulk job + ''' + normal_user = user('normal_user', False) + jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') + jt.save() + jt.execute_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}]}, normal_user, expect=400 + ).data + assert bulk_job_launch_response['__all__'][0] == 'User not part of any organization, please assign an organization to assign to the bulk job' + + +@pytest.mark.django_db +def test_bulk_job_launch_multiple_org_assigned(job_template, organization, inventory, project, credential, post, get, user): + ''' + if I am part of multiple organization... + and if I do not provide org at the launch time + ... I can't launch the bulk job + ''' + normal_user = user('normal_user', False) + org1 = Organization.objects.create(name='foo1') + org2 = Organization.objects.create(name='foo2') + org1.member_role.members.add(normal_user) + org2.member_role.members.add(normal_user) + jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') + jt.save() + jt.execute_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}]}, normal_user, expect=400 + ).data + assert bulk_job_launch_response['__all__'][0] == 'User has permission to multiple Organizations, please set one of them in the request' + + +@pytest.mark.django_db +def test_bulk_job_launch_specific_org(job_template, organization, inventory, project, credential, post, get, user): + ''' + if I am part of multiple organization... + and if I provide org at the launch time + ... I can launch the bulk job + ''' + normal_user = user('normal_user', False) + org1 = Organization.objects.create(name='foo1') + org2 = Organization.objects.create(name='foo2') + org1.member_role.members.add(normal_user) + org2.member_role.members.add(normal_user) + jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') + jt.save() + jt.execute_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}], 'organization': org1.id}, normal_user, expect=201 + ).data + bulk_job_id = bulk_job_launch_response['id'] + bulk_job_obj = WorkflowJob.objects.filter(id=bulk_job_id, is_bulk_job=True).first() + assert org1.id == bulk_job_obj.organization.id + + +@pytest.mark.django_db +def test_bulk_job_launch_inventory_no_access(job_template, organization, inventory, project, credential, post, get, user): + ''' + if I don't have access to the inventory... + and if I try to use it at the launch time + ... I can't launch the bulk job + ''' + normal_user = user('normal_user', False) + org1 = Organization.objects.create(name='foo1') + org2 = Organization.objects.create(name='foo2') + jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') + jt.save() + org1.member_role.members.add(normal_user) + inv = Inventory.objects.create(name='inv1', organization=org2) + jt.execute_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id, 'inventory': inv.id}]}, normal_user, expect=400 + ).data + assert bulk_job_launch_response['__all__'][0] == f'Inventories {{{inv.id}}} not found or you don\'t have permissions to access it' + + +@pytest.mark.django_db +def test_bulk_job_inventory_prompt(job_template, organization, inventory, project, credential, post, get, user): + ''' + Job template has an inventory set as prompt_on_launch + and if I provide the inventory as a parameter in bulk job + ... job uses that inventory + ''' + normal_user = user('normal_user', False) + org1 = Organization.objects.create(name='foo1') + jt = JobTemplate.objects.create(name='my-jt', ask_inventory_on_launch=True, project=project, playbook='helloworld.yml') + jt.save() + org1.member_role.members.add(normal_user) + inv = Inventory.objects.create(name='inv1', organization=org1) + jt.execute_role.members.add(normal_user) + inv.use_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id, 'inventory': inv.id}]}, normal_user, expect=201 + ).data + bulk_job_id = bulk_job_launch_response['id'] + node = WorkflowJob.objects.get(id=bulk_job_id).workflow_job_nodes.all().order_by('created') + assert inv.id == node[0].inventory.id + + +@pytest.mark.django_db +def test_bulk_job_set_all_prompt(job_template, organization, inventory, project, credentialtype_ssh, post, get, user): + ''' + Job template has many fields set as prompt_on_launch + and if I provide all those fields as a parameter in bulk job + ... job uses them + ''' + normal_user = user('normal_user', False) + jt = JobTemplate.objects.create( + name='my-jt', + ask_inventory_on_launch=True, + ask_diff_mode_on_launch=True, + ask_job_type_on_launch=True, + ask_verbosity_on_launch=True, + ask_execution_environment_on_launch=True, + ask_forks_on_launch=True, + ask_job_slice_count_on_launch=True, + ask_timeout_on_launch=True, + ask_variables_on_launch=True, + ask_scm_branch_on_launch=True, + ask_limit_on_launch=True, + ask_skip_tags_on_launch=True, + ask_tags_on_launch=True, + project=project, + playbook='helloworld.yml', + ) + jt.save() + organization.member_role.members.add(normal_user) + inv = Inventory.objects.create(name='inv1', organization=organization) + ee = ExecutionEnvironment.objects.create(name='test-ee', image='quay.io/foo/bar') + jt.execute_role.members.add(normal_user) + inv.use_role.members.add(normal_user) + bulk_job_launch_response = post( + reverse('api:bulk_job_launch'), + { + 'name': 'Bulk Job Launch', + 'jobs': [ + { + 'unified_job_template': jt.id, + 'inventory': inv.id, + 'diff_mode': True, + 'job_type': 'check', + 'verbosity': 3, + 'execution_environment': ee.id, + 'forks': 1, + 'job_slice_count': 1, + 'timeout': 200, + 'extra_data': {'prompted_key': 'prompted_val'}, + 'scm_branch': 'non_dev', + 'limit': 'kansas', + 'skip_tags': 'foobar', + 'job_tags': 'untagged', + } + ], + }, + normal_user, + expect=201, + ).data + bulk_job_id = bulk_job_launch_response['id'] + node = WorkflowJob.objects.get(id=bulk_job_id).workflow_job_nodes.all().order_by('created') + assert node[0].inventory.id == inv.id + assert node[0].diff_mode == True + assert node[0].job_type == 'check' + assert node[0].verbosity == 3 + assert node[0].execution_environment.id == ee.id + assert node[0].forks == 1 + assert node[0].job_slice_count == 1 + assert node[0].timeout == 200 + assert node[0].extra_data == {'prompted_key': 'prompted_val'} + assert node[0].scm_branch == 'non_dev' + assert node[0].limit == 'kansas' + assert node[0].skip_tags == 'foobar' + assert node[0].job_tags == 'untagged' + + +@pytest.mark.django_db +@pytest.mark.parametrize('num_hosts, num_queries', [(1, 70), (10, 150), (25, 250)]) +def test_bulk_host_delete_num_queries(organization, inventory, post, get, user, num_hosts, num_queries, django_assert_max_num_queries): + ''' + If I am a... + org admin + inventory admin at org level + admin of a particular inventory + superuser + + Bulk Host delete should take under a certain number of queries + ''' + users_list = setup_admin_users_list(organization, inventory, user) + for u in users_list: + hosts = [{'name': str(uuid4())} for i in range(num_hosts)] + with django_assert_max_num_queries(num_queries): + bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, u, expect=201).data + assert len(bulk_host_create_response['hosts']) == len(hosts), f"unexpected number of hosts created for user {u}" + hosts_ids_created = get_inventory_hosts(get, inventory.id, u) + bulk_host_delete_response = post(reverse('api:bulk_host_delete'), {'hosts': hosts_ids_created}, u, expect=201).data + assert len(bulk_host_delete_response['hosts'].keys()) == len(hosts), f"unexpected number of hosts deleted for user {u}" + + +@pytest.mark.django_db +def test_bulk_host_delete_rbac(organization, inventory, post, get, user): + ''' + If I am a... + org admin + inventory admin at org level + admin of a particular invenotry + ... I can bulk delete hosts + + Everyone else cannot + ''' + admin_users_list = setup_admin_users_list(organization, inventory, user) + users_list = setup_none_admin_uses_list(organization, inventory, user) + + for indx, u in enumerate(admin_users_list): + bulk_host_create_response = post( + reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': [{'name': f'foobar-{indx}'}]}, u, expect=201 + ).data + assert len(bulk_host_create_response['hosts']) == 1, f"unexpected number of hosts created for user {u}" + assert Host.objects.filter(inventory__id=inventory.id)[0].name == f'foobar-{indx}' + hosts_ids_created = get_inventory_hosts(get, inventory.id, u) + bulk_host_delete_response = post(reverse('api:bulk_host_delete'), {'hosts': hosts_ids_created}, u, expect=201).data + assert len(bulk_host_delete_response['hosts'].keys()) == 1, f"unexpected number of hosts deleted by user {u}" + + for indx, create_u in enumerate(admin_users_list): + bulk_host_create_response = post( + reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': [{'name': f'foobar2-{indx}'}]}, create_u, expect=201 + ).data + print(bulk_host_create_response) + assert bulk_host_create_response['hosts'][0]['name'] == f'foobar2-{indx}' + hosts_ids_created = get_inventory_hosts(get, inventory.id, create_u) + print(f"Try to delete {hosts_ids_created}") + for delete_u in users_list: + bulk_host_delete_response = post(reverse('api:bulk_host_delete'), {'hosts': hosts_ids_created}, delete_u, expect=403).data + assert "Lack permissions to delete hosts from this inventory." in bulk_host_delete_response['inventories'].values() + + +@pytest.mark.django_db +def test_bulk_host_delete_from_multiple_inv(organization, inventory, post, get, user): + ''' + If I am inventory admin at org level + + Bulk Host delete should be enabled only on my inventory + ''' + num_hosts = 10 + inventory.organization = organization + + # Create second inventory + inv2 = organization.inventories.create(name="second-test-inv") + inv2.organization = organization + admin2_user = user('inventory2_admin', False) + inv2.admin_role.members.add(admin2_user) + + admin_user = user('inventory_admin', False) + inventory.admin_role.members.add(admin_user) + + organization.member_role.members.add(admin_user) + organization.member_role.members.add(admin2_user) + + hosts = [{'name': str(uuid4())} for i in range(num_hosts)] + hosts2 = [{'name': str(uuid4())} for i in range(num_hosts)] + + # create hosts in each of the inventories + bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, admin_user, expect=201).data + assert len(bulk_host_create_response['hosts']) == len(hosts), f"unexpected number of hosts created for user {admin_user}" + + bulk_host_create_response2 = post(reverse('api:bulk_host_create'), {'inventory': inv2.id, 'hosts': hosts2}, admin2_user, expect=201).data + assert len(bulk_host_create_response2['hosts']) == len(hosts), f"unexpected number of hosts created for user {admin2_user}" + + # get all hosts ids - from both inventories + hosts_ids_created = get_inventory_hosts(get, inventory.id, admin_user) + hosts_ids_created += get_inventory_hosts(get, inv2.id, admin2_user) + + expected_error = "Lack permissions to delete hosts from this inventory." + # try to delete ALL hosts with admin user of inventory 1. + for inv_name, invadmin in zip([inv2.name, inventory.name], [admin_user, admin2_user]): + bulk_host_delete_response = post(reverse('api:bulk_host_delete'), {'hosts': hosts_ids_created}, invadmin, expect=403).data + result_message = bulk_host_delete_response['inventories'][inv_name] + assert result_message == expected_error, f"deleted hosts without permission by user {invadmin}" + + +def setup_admin_users_list(organization, inventory, user): + inventory.organization = organization + inventory_admin = user('inventory_admin', False) + org_admin = user('org_admin', False) + org_inv_admin = user('org_admin', False) + superuser = user('admin', True) + for u in [org_admin, org_inv_admin, inventory_admin]: + organization.member_role.members.add(u) + organization.admin_role.members.add(org_admin) + organization.inventory_admin_role.members.add(org_inv_admin) + inventory.admin_role.members.add(inventory_admin) + return [inventory_admin, org_inv_admin, superuser, org_admin] + + +def setup_none_admin_uses_list(organization, inventory, user): + inventory.organization = organization + auditor = user('auditor', False) + member = user('member', False) + use_inv_member = user('member', False) + for u in [auditor, member, use_inv_member]: + organization.member_role.members.add(u) + inventory.use_role.members.add(use_inv_member) + organization.auditor_role.members.add(auditor) + return [auditor, member, use_inv_member] + + +def get_inventory_hosts(get, inv_id, use_user): + data = get(reverse('api:inventory_hosts_list', kwargs={'pk': inv_id}), use_user, expect=200).data + results = [host['id'] for host in data['results']] + return results diff --git a/awx/main/tests/functional/test_copy.py b/awx/main/tests/functional/test_copy.py index 0574f9ccbdef..7c156979a8b7 100644 --- a/awx/main/tests/functional/test_copy.py +++ b/awx/main/tests/functional/test_copy.py @@ -43,7 +43,7 @@ def test_job_template_copy( c.save() assert get(reverse('api:job_template_copy', kwargs={'pk': job_template_with_survey_passwords.pk}), alice, expect=200).data['can_copy'] is True jt_copy_pk_alice = post( - reverse('api:job_template_copy', kwargs={'pk': job_template_with_survey_passwords.pk}), {'name': 'new jt name'}, alice, expect=201 + reverse('api:job_template_copy', kwargs={'pk': job_template_with_survey_passwords.pk}), {'name': 'new jt name alice'}, alice, expect=201 ).data['id'] jt_copy_admin = type(job_template_with_survey_passwords).objects.get(pk=jt_copy_pk) @@ -53,7 +53,7 @@ def test_job_template_copy( assert jt_copy_alice.created_by == alice for jt_copy in (jt_copy_admin, jt_copy_alice): - assert jt_copy.name == 'new jt name' + assert jt_copy.name.startswith('new jt name') assert jt_copy.project == project assert jt_copy.inventory == inventory assert jt_copy.playbook == job_template_with_survey_passwords.playbook @@ -123,6 +123,24 @@ def test_inventory_copy(inventory, group_factory, post, get, alice, organization assert set(group_2_2_copy.hosts.all()) == set() +@pytest.mark.django_db +@pytest.mark.parametrize( + "is_admin, can_copy, status", + [ + [True, True, 200], + [False, False, 200], + ], +) +def test_workflow_job_template_copy_access(get, admin_user, alice, workflow_job_template, is_admin, can_copy, status): + url = reverse('api:workflow_job_template_copy', kwargs={'pk': workflow_job_template.pk}) + if is_admin: + response = get(url, user=admin_user, expect=status) + else: + workflow_job_template.organization.auditor_role.members.add(alice) + response = get(url, user=alice, expect=status) + assert response.data['can_copy'] == can_copy + + @pytest.mark.django_db def test_workflow_job_template_copy(workflow_job_template, post, get, admin, organization): ''' diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index d2937412daa5..5d2106dbab22 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -74,42 +74,64 @@ @pytest.mark.django_db def test_default_cred_types(): - assert sorted(CredentialType.defaults.keys()) == sorted( - [ - 'aim', - 'aws', - 'azure_kv', - 'azure_rm', - 'centrify_vault_kv', - 'conjur', - 'controller', - 'galaxy_api_token', - 'gce', - 'github_token', - 'gitlab_token', - 'gpg_public_key', - 'hashivault_kv', - 'hashivault_ssh', - 'insights', - 'kubernetes_bearer_token', - 'net', - 'openstack', - 'registry', - 'rhv', - 'satellite6', - 'scm', - 'ssh', - 'thycotic_dsv', - 'thycotic_tss', - 'vault', - 'vmware', - ] - ) + expected = [ + 'aim', + 'aws', + 'aws_secretsmanager_credential', + 'azure_kv', + 'azure_rm', + 'bitbucket_dc_token', + 'centrify_vault_kv', + 'conjur', + 'controller', + 'galaxy_api_token', + 'gce', + 'github_token', + 'github_app_lookup', + 'gitlab_token', + 'gpg_public_key', + 'hashivault_kv', + 'hashivault_ssh', + 'hcp_terraform', + 'insights', + 'kubernetes_bearer_token', + 'net', + 'openstack', + 'registry', + 'rhv', + 'satellite6', + 'scm', + 'ssh', + 'terraform', + 'thycotic_dsv', + 'thycotic_tss', + 'vault', + 'vmware', + ] + assert sorted(CredentialType.defaults.keys()) == sorted(expected) + assert 'hashivault-kv-oidc' not in CredentialType.defaults + assert 'hashivault-ssh-oidc' not in CredentialType.defaults for type_ in CredentialType.defaults.values(): assert type_().managed is True +@pytest.mark.django_db +def test_default_cred_types_with_oidc_enabled(): + from django.test import override_settings + from awx.main.models.credential import load_credentials, ManagedCredentialType + + original_registry = ManagedCredentialType.registry.copy() + try: + with override_settings(FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED=True): + ManagedCredentialType.registry.clear() + load_credentials() + assert 'hashivault-kv-oidc' in CredentialType.defaults + assert 'hashivault-ssh-oidc' in CredentialType.defaults + finally: + ManagedCredentialType.registry = original_registry + + @pytest.mark.django_db def test_credential_creation(organization_factory): org = organization_factory('test').organization @@ -336,3 +358,25 @@ def test_credential_get_input(organization_factory): # verify return values for encrypted secret fields are decrypted assert cred.inputs['vault_password'].startswith('$encrypted$') assert cred.get_input('vault_password') == 'testing321' + + +@pytest.mark.django_db +def test_idempotent_credential_type_setup(): + """ + awx main app ready() calls `setup_tower_managed_defaults()` to register CredentialType(s). + This is problematic in our testing system. pytest_django deviates from the production ready() call path. pytest_django calls our apps ready() function + before migrations run. This is a problem since we interact with tables in the database that do not yet exist. + + Now forget about what you just read because we do not _actually_ want to register CredentialType(s) in our test at all. So then + you would expect this bit of code to spy on `setup_tower_managed_defaults` and assert it was not called BUT registering a spy early + enough is hard. The call to ready() from pytest_django happens via pytest hooks very early https://github.com/pytest-dev/pytest-django/blob/1157a7c5c74f4b4e0f4aca8312f3fe67eb00568e/pytest_django/plugin.py#L266C5-L266C34 + + Instead of ensuring that `setup_tower_managed_defaults()` is explicitly not called, we check it _implicitly_ by observing that no credential type records are created. + """ + assert CredentialType.objects.count() == 0 + CredentialType.setup_tower_managed_defaults() + total = CredentialType.objects.count() + assert total > 0 + + CredentialType.setup_tower_managed_defaults() + assert CredentialType.objects.count() == total diff --git a/awx/main/tests/functional/test_credential_plugins.py b/awx/main/tests/functional/test_credential_plugins.py deleted file mode 100644 index 3ea31fce425d..000000000000 --- a/awx/main/tests/functional/test_credential_plugins.py +++ /dev/null @@ -1,78 +0,0 @@ -import pytest -from unittest import mock -from awx.main.credential_plugins import hashivault - - -def test_imported_azure_cloud_sdk_vars(): - from awx.main.credential_plugins import azure_kv - - assert len(azure_kv.clouds) > 0 - assert all([hasattr(c, 'name') for c in azure_kv.clouds]) - assert all([hasattr(c, 'suffixes') for c in azure_kv.clouds]) - assert all([hasattr(c.suffixes, 'keyvault_dns') for c in azure_kv.clouds]) - - -def test_hashivault_approle_auth(): - kwargs = { - 'role_id': 'the_role_id', - 'secret_id': 'the_secret_id', - } - expected_res = { - 'role_id': 'the_role_id', - 'secret_id': 'the_secret_id', - } - res = hashivault.approle_auth(**kwargs) - assert res == expected_res - - -def test_hashivault_kubernetes_auth(): - kwargs = { - 'kubernetes_role': 'the_kubernetes_role', - } - expected_res = { - 'role': 'the_kubernetes_role', - 'jwt': 'the_jwt', - } - with mock.patch('pathlib.Path') as path_mock: - mock.mock_open(path_mock.return_value.open, read_data='the_jwt') - res = hashivault.kubernetes_auth(**kwargs) - path_mock.assert_called_with('/var/run/secrets/kubernetes.io/serviceaccount/token') - assert res == expected_res - - -def test_hashivault_handle_auth_token(): - kwargs = { - 'token': 'the_token', - } - token = hashivault.handle_auth(**kwargs) - assert token == kwargs['token'] - - -def test_hashivault_handle_auth_approle(): - kwargs = { - 'role_id': 'the_role_id', - 'secret_id': 'the_secret_id', - } - with mock.patch.object(hashivault, 'method_auth') as method_mock: - method_mock.return_value = 'the_token' - token = hashivault.handle_auth(**kwargs) - method_mock.assert_called_with(**kwargs, auth_param=kwargs) - assert token == 'the_token' - - -def test_hashivault_handle_auth_kubernetes(): - kwargs = { - 'kubernetes_role': 'the_kubernetes_role', - } - with mock.patch.object(hashivault, 'method_auth') as method_mock: - with mock.patch('pathlib.Path') as path_mock: - mock.mock_open(path_mock.return_value.open, read_data='the_jwt') - method_mock.return_value = 'the_token' - token = hashivault.handle_auth(**kwargs) - method_mock.assert_called_with(**kwargs, auth_param={'role': 'the_kubernetes_role', 'jwt': 'the_jwt'}) - assert token == 'the_token' - - -def test_hashivault_handle_auth_not_enough_args(): - with pytest.raises(Exception): - hashivault.handle_auth() diff --git a/awx/main/tests/functional/test_dispatch.py b/awx/main/tests/functional/test_dispatch.py index 86e90e50a0f4..7226318a19bf 100644 --- a/awx/main/tests/functional/test_dispatch.py +++ b/awx/main/tests/functional/test_dispatch.py @@ -1,19 +1,12 @@ import datetime -import multiprocessing -import random -import signal -import time from unittest import mock - from django.utils.timezone import now as tz_now import pytest from awx.main.models import Job, WorkflowJob, Instance from awx.main.dispatch import reaper -from awx.main.dispatch.pool import StatefulPoolWorker, WorkerPool, AutoscalePool -from awx.main.dispatch.publish import task -from awx.main.dispatch.worker import BaseWorker, TaskWorker - +from awx.main.tasks import system +from dispatcherd.publish import task ''' Prevent logger. calls from triggering database operations @@ -56,286 +49,9 @@ def multiply(a, b): return a * b -class SimpleWorker(BaseWorker): - def perform_work(self, body, *args): - pass - - -class ResultWriter(BaseWorker): - def perform_work(self, body, result_queue): - result_queue.put(body + '!!!') - - -class SlowResultWriter(BaseWorker): - def perform_work(self, body, result_queue): - time.sleep(3) - super(SlowResultWriter, self).perform_work(body, result_queue) - - -@pytest.mark.usefixtures("disable_database_settings") -class TestPoolWorker: - def setup_method(self, test_method): - self.worker = StatefulPoolWorker(1000, self.tick, tuple()) - - def tick(self): - self.worker.finished.put(self.worker.queue.get()['uuid']) - time.sleep(0.5) - - def test_qsize(self): - assert self.worker.qsize == 0 - for i in range(3): - self.worker.put({'task': 'abc123'}) - assert self.worker.qsize == 3 - - def test_put(self): - assert len(self.worker.managed_tasks) == 0 - assert self.worker.messages_finished == 0 - self.worker.put({'task': 'abc123'}) - - assert len(self.worker.managed_tasks) == 1 - assert self.worker.messages_sent == 1 - - def test_managed_tasks(self): - self.worker.put({'task': 'abc123'}) - self.worker.calculate_managed_tasks() - assert len(self.worker.managed_tasks) == 1 - - self.tick() - self.worker.calculate_managed_tasks() - assert len(self.worker.managed_tasks) == 0 - - def test_current_task(self): - self.worker.put({'task': 'abc123'}) - assert self.worker.current_task['task'] == 'abc123' - - def test_quit(self): - self.worker.quit() - assert self.worker.queue.get() == 'QUIT' - - def test_idle_busy(self): - assert self.worker.idle is True - assert self.worker.busy is False - self.worker.put({'task': 'abc123'}) - assert self.worker.busy is True - assert self.worker.idle is False - - -@pytest.mark.django_db -class TestWorkerPool: - def setup_method(self, test_method): - self.pool = WorkerPool(min_workers=3) - - def teardown_method(self, test_method): - self.pool.stop(signal.SIGTERM) - - def test_worker(self): - self.pool.init_workers(SimpleWorker().work_loop) - assert len(self.pool) == 3 - for worker in self.pool.workers: - assert worker.messages_sent == 0 - assert worker.alive is True - - def test_single_task(self): - self.pool.init_workers(SimpleWorker().work_loop) - self.pool.write(0, 'xyz') - assert self.pool.workers[0].messages_sent == 1 # worker at index 0 handled one task - assert self.pool.workers[1].messages_sent == 0 - assert self.pool.workers[2].messages_sent == 0 - - def test_queue_preference(self): - self.pool.init_workers(SimpleWorker().work_loop) - self.pool.write(2, 'xyz') - assert self.pool.workers[0].messages_sent == 0 - assert self.pool.workers[1].messages_sent == 0 - assert self.pool.workers[2].messages_sent == 1 # worker at index 2 handled one task - - def test_worker_processing(self): - result_queue = multiprocessing.Queue() - self.pool.init_workers(ResultWriter().work_loop, result_queue) - for i in range(10): - self.pool.write(random.choice(range(len(self.pool))), 'Hello, Worker {}'.format(i)) - all_messages = [result_queue.get(timeout=1) for i in range(10)] - all_messages.sort() - assert all_messages == ['Hello, Worker {}!!!'.format(i) for i in range(10)] - - total_handled = sum([worker.messages_sent for worker in self.pool.workers]) - assert total_handled == 10 - - -@pytest.mark.django_db -class TestAutoScaling: - def setup_method(self, test_method): - self.pool = AutoscalePool(min_workers=2, max_workers=10) - - def teardown_method(self, test_method): - self.pool.stop(signal.SIGTERM) - - def test_scale_up(self): - result_queue = multiprocessing.Queue() - self.pool.init_workers(SlowResultWriter().work_loop, result_queue) - - # start with two workers, write an event to each worker and make it busy - assert len(self.pool) == 2 - for i, w in enumerate(self.pool.workers): - w.put('Hello, Worker {}'.format(0)) - assert len(self.pool) == 2 - - # wait for the subprocesses to start working on their tasks and be marked busy - time.sleep(1) - assert self.pool.should_grow - - # write a third message, expect a new worker to spawn because all - # workers are busy - self.pool.write(0, 'Hello, Worker {}'.format(2)) - assert len(self.pool) == 3 - - def test_scale_down(self): - self.pool.init_workers(ResultWriter().work_loop, multiprocessing.Queue()) - - # start with two workers, and scale up to 10 workers - assert len(self.pool) == 2 - for i in range(8): - self.pool.up() - assert len(self.pool) == 10 - - # cleanup should scale down to 8 workers - self.pool.cleanup() - assert len(self.pool) == 2 - - def test_max_scale_up(self): - self.pool.init_workers(ResultWriter().work_loop, multiprocessing.Queue()) - - assert len(self.pool) == 2 - for i in range(25): - self.pool.up() - assert self.pool.max_workers == 10 - assert self.pool.full is True - assert len(self.pool) == 10 - - def test_equal_worker_distribution(self): - # if all workers are busy, spawn new workers *before* adding messages - # to an existing queue - self.pool.init_workers(SlowResultWriter().work_loop, multiprocessing.Queue) - - # start with two workers, write an event to each worker and make it busy - assert len(self.pool) == 2 - for i in range(10): - self.pool.write(0, 'Hello, World!') - assert len(self.pool) == 10 - for w in self.pool.workers: - assert w.busy - assert len(w.managed_tasks) == 1 - - # the queue is full at 10, the _next_ write should put the message into - # a worker's backlog - assert len(self.pool) == 10 - for w in self.pool.workers: - assert w.messages_sent == 1 - self.pool.write(0, 'Hello, World!') - assert len(self.pool) == 10 - assert self.pool.workers[0].messages_sent == 2 - - def test_lost_worker_autoscale(self): - # if a worker exits, it should be replaced automatically up to min_workers - self.pool.init_workers(ResultWriter().work_loop, multiprocessing.Queue()) - - # start with two workers, kill one of them - assert len(self.pool) == 2 - assert not self.pool.should_grow - alive_pid = self.pool.workers[1].pid - self.pool.workers[0].process.terminate() - time.sleep(2) # wait a moment for sigterm - - # clean up and the dead worker - self.pool.cleanup() - assert len(self.pool) == 1 - assert self.pool.workers[0].pid == alive_pid - - # the next queue write should replace the lost worker - self.pool.write(0, 'Hello, Worker') - assert len(self.pool) == 2 - - -@pytest.mark.usefixtures("disable_database_settings") -class TestTaskDispatcher: - @property - def tm(self): - return TaskWorker() - - def test_function_dispatch(self): - result = self.tm.perform_work({'task': 'awx.main.tests.functional.test_dispatch.add', 'args': [2, 2]}) - assert result == 4 - - def test_function_dispatch_must_be_decorated(self): - result = self.tm.perform_work({'task': 'awx.main.tests.functional.test_dispatch.restricted', 'args': [2, 2]}) - assert isinstance(result, ValueError) - assert str(result) == 'awx.main.tests.functional.test_dispatch.restricted is not decorated with @task()' # noqa - - def test_method_dispatch(self): - result = self.tm.perform_work({'task': 'awx.main.tests.functional.test_dispatch.Adder', 'args': [2, 2]}) - assert result == 4 - - def test_method_dispatch_must_be_decorated(self): - result = self.tm.perform_work({'task': 'awx.main.tests.functional.test_dispatch.Restricted', 'args': [2, 2]}) - assert isinstance(result, ValueError) - assert str(result) == 'awx.main.tests.functional.test_dispatch.Restricted is not decorated with @task()' # noqa - - def test_python_function_cannot_be_imported(self): - result = self.tm.perform_work( - { - 'task': 'os.system', - 'args': ['ls'], - } - ) - assert isinstance(result, ValueError) - assert str(result) == 'os.system is not a valid awx task' # noqa - - def test_undefined_function_cannot_be_imported(self): - result = self.tm.perform_work({'task': 'awx.foo.bar'}) - assert isinstance(result, ModuleNotFoundError) - assert str(result) == "No module named 'awx.foo'" # noqa - - -class TestTaskPublisher: - def test_function_callable(self): - assert add(2, 2) == 4 - - def test_method_callable(self): - assert Adder().run(2, 2) == 4 - - def test_function_apply_async(self): - message, queue = add.apply_async([2, 2], queue='foobar') - assert message['args'] == [2, 2] - assert message['kwargs'] == {} - assert message['task'] == 'awx.main.tests.functional.test_dispatch.add' - assert queue == 'foobar' - - def test_method_apply_async(self): - message, queue = Adder.apply_async([2, 2], queue='foobar') - assert message['args'] == [2, 2] - assert message['kwargs'] == {} - assert message['task'] == 'awx.main.tests.functional.test_dispatch.Adder' - assert queue == 'foobar' - - def test_apply_async_queue_required(self): - with pytest.raises(ValueError) as e: - message, queue = add.apply_async([2, 2]) - assert "awx.main.tests.functional.test_dispatch.add: Queue value required and may not be None" == e.value.args[0] - - def test_queue_defined_in_task_decorator(self): - message, queue = multiply.apply_async([2, 2]) - assert queue == 'hard-math' - - def test_queue_overridden_from_task_decorator(self): - message, queue = multiply.apply_async([2, 2], queue='not-so-hard') - assert queue == 'not-so-hard' - - def test_apply_with_callable_queuename(self): - message, queue = add.apply_async([2, 2], queue=lambda: 'called') - assert queue == 'called' - - yesterday = tz_now() - datetime.timedelta(days=1) +minute = tz_now() - datetime.timedelta(seconds=120) +now = tz_now() @pytest.mark.django_db @@ -346,11 +62,6 @@ class TestJobReaper(object): ('running', '', '', None, False), # running, not assigned to the instance ('running', 'awx', '', None, True), # running, has the instance as its execution_node ('running', '', 'awx', None, True), # running, has the instance as its controller_node - ('waiting', '', '', None, False), # waiting, not assigned to the instance - ('waiting', 'awx', '', None, False), # waiting, was edited less than a minute ago - ('waiting', '', 'awx', None, False), # waiting, was edited less than a minute ago - ('waiting', 'awx', '', yesterday, False), # waiting, managed by another node, ignore - ('waiting', '', 'awx', yesterday, True), # waiting, assigned to the controller_node, stale ], ) def test_should_reap(self, status, fail, execution_node, controller_node, modified): @@ -368,7 +79,6 @@ def test_should_reap(self, status, fail, execution_node, controller_node, modifi # (because .save() overwrites it to _now_) Job.objects.filter(id=j.id).update(modified=modified) reaper.reap(i) - reaper.reap_waiting(i) job = Job.objects.first() if fail: assert job.status == 'failed' @@ -377,14 +87,30 @@ def test_should_reap(self, status, fail, execution_node, controller_node, modifi else: assert job.status == status + def test_waiting_job_sent_back_to_pending(self): + this_inst = Instance(hostname='awx') + this_inst.save() + lost_inst = Instance(hostname='lost', node_type=Instance.Types.EXECUTION, node_state=Instance.States.UNAVAILABLE) + lost_inst.save() + job = Job.objects.create(status='waiting', controller_node=lost_inst.hostname, execution_node='lost') + + system._heartbeat_handle_lost_instances([lost_inst], this_inst) + job.refresh_from_db() + + assert job.status == 'pending' + assert job.controller_node == '' + assert job.execution_node == '' + @pytest.mark.parametrize( - 'excluded_uuids, fail', + 'excluded_uuids, fail, started', [ - (['abc123'], False), - ([], True), + (['abc123'], False, None), + ([], False, None), + ([], True, minute), ], ) - def test_do_not_reap_excluded_uuids(self, excluded_uuids, fail): + def test_do_not_reap_excluded_uuids(self, excluded_uuids, fail, started): + """Modified Test to account for ref_time in reap()""" i = Instance(hostname='awx') i.save() j = Job( @@ -395,10 +121,13 @@ def test_do_not_reap_excluded_uuids(self, excluded_uuids, fail): celery_task_id='abc123', ) j.save() + if started: + Job.objects.filter(id=j.id).update(started=started) # if the UUID is excluded, don't reap it - reaper.reap(i, excluded_uuids=excluded_uuids) + reaper.reap(i, excluded_uuids=excluded_uuids, ref_time=now) job = Job.objects.first() + if fail: assert job.status == 'failed' assert 'marked as failed' in job.job_explanation @@ -414,3 +143,20 @@ def test_workflow_does_not_reap(self): reaper.reap(i) assert WorkflowJob.objects.first().status == 'running' + + def test_should_not_reap_new(self): + """ + This test is designed specifically to ensure that jobs that are launched after the dispatcher has provided a list of UUIDs aren't reaped. + It is very racy and this test is designed with that in mind + """ + i = Instance(hostname='awx') + # ref_time is set to 10 seconds in the past to mimic someone launching a job in the heartbeat window. + ref_time = tz_now() - datetime.timedelta(seconds=10) + # creating job at current time + job = Job.objects.create(status='running', controller_node=i.hostname) + reaper.reap(i, ref_time=ref_time) + # explictly refreshing from db to ensure up to date cache + job.refresh_from_db() + assert job.started > ref_time + assert job.status == 'running' + assert job.job_explanation == '' diff --git a/awx/main/tests/functional/test_fixture_factories.py b/awx/main/tests/functional/test_fixture_factories.py index 1af7b6624650..30fa1247ae9a 100644 --- a/awx/main/tests/functional/test_fixture_factories.py +++ b/awx/main/tests/functional/test_fixture_factories.py @@ -50,13 +50,11 @@ def test_org_factory_roles(organization_factory): teams=['team1', 'team2'], users=['team1:foo', 'bar'], projects=['baz', 'bang'], - roles=['team2.member_role:foo', 'team1.admin_role:bar', 'team1.admin_role:team2.admin_role', 'baz.admin_role:foo'], + roles=['team2.member_role:foo', 'team1.admin_role:bar', 'baz.admin_role:foo'], ) - - assert objects.users.bar in objects.teams.team2.admin_role + assert objects.users.bar in objects.teams.team1.admin_role assert objects.users.foo in objects.projects.baz.admin_role assert objects.users.foo in objects.teams.team1.member_role - assert objects.teams.team2.admin_role in objects.teams.team1.admin_role.children.all() @pytest.mark.django_db diff --git a/awx/main/tests/functional/test_ha.py b/awx/main/tests/functional/test_ha.py deleted file mode 100644 index 0b4ced53c218..000000000000 --- a/awx/main/tests/functional/test_ha.py +++ /dev/null @@ -1,19 +0,0 @@ -import pytest - -# AWX -from awx.main.ha import is_ha_environment -from awx.main.models.ha import Instance - - -@pytest.mark.django_db -def test_multiple_instances(): - for i in range(2): - Instance.objects.create(hostname=f'foo{i}', node_type='hybrid') - assert is_ha_environment() - - -@pytest.mark.django_db -def test_db_localhost(): - Instance.objects.create(hostname='foo', node_type='hybrid') - Instance.objects.create(hostname='bar', node_type='execution') - assert is_ha_environment() is False diff --git a/awx/main/tests/functional/test_instance_group_ordering.py b/awx/main/tests/functional/test_instance_group_ordering.py index 42c69ffc7fe0..fb8c0db16858 100644 --- a/awx/main/tests/functional/test_instance_group_ordering.py +++ b/awx/main/tests/functional/test_instance_group_ordering.py @@ -1,6 +1,6 @@ import pytest -from awx.main.models import InstanceGroup +from awx.main.models import InstanceGroup, Inventory @pytest.fixture(scope='function') @@ -38,6 +38,16 @@ def test_instance_group_ordering(source_model): assert source_model.instance_groups.through.objects.count() == 0 +@pytest.mark.django_db +@pytest.mark.parametrize('source_model', ['job_template', 'inventory', 'organization'], indirect=True) +def test_instance_group_bulk_add(source_model): + groups = [InstanceGroup.objects.create(name='host-%d' % i) for i in range(5)] + groups.reverse() + with pytest.raises(RuntimeError) as err: + source_model.instance_groups.add(*groups) + assert 'Ordered many-to-many fields do not support multiple objects' in str(err) + + @pytest.mark.django_db @pytest.mark.parametrize('source_model', ['job_template', 'inventory', 'organization'], indirect=True) def test_instance_group_middle_deletion(source_model): @@ -66,3 +76,33 @@ def test_explicit_ordering(source_model): assert [g.name for g in source_model.instance_groups.all()] == ['host-4', 'host-3', 'host-2', 'host-1', 'host-0'] assert [g.name for g in source_model.instance_groups.order_by('name').all()] == ['host-0', 'host-1', 'host-2', 'host-3', 'host-4'] + + +@pytest.mark.django_db +def test_input_inventories_ordering(): + constructed_inventory = Inventory.objects.create(name='my_constructed', kind='constructed') + input_inventories = [Inventory.objects.create(name='inv-%d' % i) for i in range(5)] + input_inventories.reverse() + for inv in input_inventories: + constructed_inventory.input_inventories.add(inv) + + assert [g.name for g in constructed_inventory.input_inventories.all()] == ['inv-4', 'inv-3', 'inv-2', 'inv-1', 'inv-0'] + assert [(row.position, row.input_inventory.name) for row in constructed_inventory.input_inventories.through.objects.all()] == [ + (0, 'inv-4'), + (1, 'inv-3'), + (2, 'inv-2'), + (3, 'inv-1'), + (4, 'inv-0'), + ] + + constructed_inventory.input_inventories.remove(input_inventories[0]) + assert [g.name for g in constructed_inventory.input_inventories.all()] == ['inv-3', 'inv-2', 'inv-1', 'inv-0'] + assert [(row.position, row.input_inventory.name) for row in constructed_inventory.input_inventories.through.objects.all()] == [ + (0, 'inv-3'), + (1, 'inv-2'), + (2, 'inv-1'), + (3, 'inv-0'), + ] + + constructed_inventory.input_inventories.clear() + assert constructed_inventory.input_inventories.through.objects.count() == 0 diff --git a/awx/main/tests/functional/test_instances.py b/awx/main/tests/functional/test_instances.py index df6d17786819..4ed652179d91 100644 --- a/awx/main/tests/functional/test_instances.py +++ b/awx/main/tests/functional/test_instances.py @@ -94,17 +94,18 @@ def test_instance_dup(org_admin, organization, project, instance_factory, instan ig_all = instance_group_factory("all", instances=[i1, i2, i3]) ig_dup = instance_group_factory("duplicates", instances=[i1]) - project.organization.instance_groups.add(ig_all, ig_dup) + project.organization.instance_groups.add(ig_all) + project.organization.instance_groups.add(ig_dup) actual_num_instances = Instance.objects.count() list_response = get(reverse('api:instance_list'), user=system_auditor) api_num_instances_auditor = list(list_response.data.items())[0][1] + ig_all.read_role.members.add(org_admin) list_response2 = get(reverse('api:instance_list'), user=org_admin) api_num_instances_oa = list(list_response2.data.items())[0][1] assert api_num_instances_auditor == actual_num_instances - # Note: The org_admin will not see the default 'tower' node - # (instance fixture) because it is not in its group, as expected + # Note: The org_admin will not see instances unless at least read_role to the IG has been assigned assert api_num_instances_oa == (actual_num_instances - 1) diff --git a/awx/main/tests/functional/test_inventory_input_constructed.py b/awx/main/tests/functional/test_inventory_input_constructed.py new file mode 100644 index 000000000000..2602cf294777 --- /dev/null +++ b/awx/main/tests/functional/test_inventory_input_constructed.py @@ -0,0 +1,61 @@ +import pytest +from awx.main.models import Inventory +from awx.api.versioning import reverse + + +@pytest.mark.django_db +def test_constructed_inventory_post(post, admin_user, organization): + inv1 = Inventory.objects.create(name='dummy1', kind='constructed', organization=organization) + inv2 = Inventory.objects.create(name='dummy2', kind='constructed', organization=organization) + resp = post( + url=reverse('api:inventory_input_inventories', kwargs={'pk': inv1.pk}), + data={'id': inv2.pk}, + user=admin_user, + expect=400, + ) + assert resp.status_code == 400 + + +@pytest.mark.django_db +def test_add_constructed_inventory_source(post, admin_user, constructed_inventory): + resp = post( + url=reverse('api:inventory_inventory_sources_list', kwargs={'pk': constructed_inventory.pk}), + data={'name': 'dummy1', 'source': 'constructed'}, + user=admin_user, + expect=400, + ) + assert resp.status_code == 400 + + +@pytest.mark.django_db +def test_add_constructed_inventory_host(post, admin_user, constructed_inventory): + resp = post( + url=reverse('api:inventory_hosts_list', kwargs={'pk': constructed_inventory.pk}), + data={'name': 'dummy1'}, + user=admin_user, + expect=400, + ) + assert resp.status_code == 400 + + +@pytest.mark.django_db +def test_add_constructed_inventory_group(post, admin_user, constructed_inventory): + resp = post( + reverse('api:inventory_groups_list', kwargs={'pk': constructed_inventory.pk}), + data={'name': 'group-test'}, + user=admin_user, + expect=400, + ) + assert resp.status_code == 400 + + +@pytest.mark.django_db +def test_edit_constructed_inventory_source(patch, admin_user, inventory_source_factory): + inv_src = inventory_source_factory(name='dummy1', source='constructed') + resp = patch( + reverse('api:inventory_source_detail', kwargs={'pk': inv_src.pk}), + data={'description': inv_src.name}, + user=admin_user, + expect=400, + ) + assert resp.status_code == 400 diff --git a/awx/main/tests/functional/test_inventory_source_injectors.py b/awx/main/tests/functional/test_inventory_source_injectors.py index 97fc8a7c17cc..8f740db72f72 100644 --- a/awx/main/tests/functional/test_inventory_source_injectors.py +++ b/awx/main/tests/functional/test_inventory_source_injectors.py @@ -5,12 +5,13 @@ import re from collections import namedtuple +from awx_plugins.interfaces._temporary_private_container_api import get_incontainer_path + from awx.main.tasks.jobs import RunInventoryUpdate from awx.main.models import InventorySource, Credential, CredentialType, UnifiedJob, ExecutionEnvironment -from awx.main.constants import CLOUD_PROVIDERS, STANDARD_INVENTORY_UPDATE_ENV +from awx.main.constants import STANDARD_INVENTORY_UPDATE_ENV from awx.main.tests import data -from awx.main.utils.execution_environments import to_container_path - +from awx.main.utils.plugins import discover_available_cloud_provider_plugin_names from django.conf import settings DATA = os.path.join(os.path.dirname(data.__file__), 'inventory') @@ -46,6 +47,8 @@ def generate_fake_var(element): def credential_kind(source): """Given the inventory source kind, return expected credential kind""" + if source == 'openshift_virtualization': + return 'kubernetes_bearer_token' return source.replace('ec2', 'aws') @@ -107,12 +110,13 @@ def read_content(private_data_dir, raw_env, inventory_update): for filename in os.listdir(os.path.join(private_data_dir, subdir)): filename_list.append(os.path.join(subdir, filename)) filename_list = sorted(filename_list, key=lambda fn: inverse_env.get(os.path.join(private_data_dir, fn), [fn])[0]) + inventory_content = "" for filename in filename_list: if filename in ('args', 'project'): continue # Ansible runner abs_file_path = os.path.join(private_data_dir, filename) file_aliases[abs_file_path] = filename - runner_path = to_container_path(abs_file_path, private_data_dir) + runner_path = get_incontainer_path(abs_file_path, private_data_dir) if runner_path in inverse_env: referenced_paths.add(abs_file_path) alias = 'file_reference' @@ -121,7 +125,7 @@ def read_content(private_data_dir, raw_env, inventory_update): break alias = 'file_reference_{}'.format(i) else: - raise RuntimeError('Test not able to cope with >10 references by env vars. ' 'Something probably went very wrong.') + raise RuntimeError('Test not able to cope with >10 references by env vars. Something probably went very wrong.') file_aliases[abs_file_path] = alias for env_key in inverse_env[runner_path]: env[env_key] = '{{{{ {} }}}}'.format(alias) @@ -130,6 +134,7 @@ def read_content(private_data_dir, raw_env, inventory_update): dir_contents[abs_file_path] = f.read() # Declare a reference to inventory plugin file if it exists if abs_file_path.endswith('.yml') and 'plugin: ' in dir_contents[abs_file_path]: + inventory_content = dir_contents[abs_file_path] referenced_paths.add(abs_file_path) # used as inventory file elif cache_file_regex.match(abs_file_path): file_aliases[abs_file_path] = 'cache_file' @@ -157,7 +162,11 @@ def read_content(private_data_dir, raw_env, inventory_update): content = {} for abs_file_path, file_content in dir_contents.items(): # assert that all files laid down are used - if abs_file_path not in referenced_paths and abs_file_path not in ignore_files: + if ( + abs_file_path not in referenced_paths + and get_incontainer_path(abs_file_path, private_data_dir) not in inventory_content + and abs_file_path not in ignore_files + ): raise AssertionError( "File {} is not referenced. References and files:\n{}\n{}".format(abs_file_path, json.dumps(env, indent=4), json.dumps(dir_contents, indent=4)) ) @@ -182,15 +191,14 @@ def create_reference_data(source_dir, env, content): json.dump(env, f, indent=4, sort_keys=True) +@mock.patch('awx_plugins.interfaces._temporary_private_licensing_api.detect_server_product_name', return_value='NOT-AWX') @pytest.mark.django_db -@pytest.mark.parametrize('this_kind', CLOUD_PROVIDERS) -def test_inventory_update_injected_content(this_kind, inventory, fake_credential_factory, mock_me): +@pytest.mark.parametrize('this_kind', discover_available_cloud_provider_plugin_names()) +def test_inventory_update_injected_content(product_name, this_kind, inventory, fake_credential_factory, mock_me): ExecutionEnvironment.objects.create(name='Control Plane EE', managed=True) ExecutionEnvironment.objects.create(name='Default Job EE', managed=False) injector = InventorySource.injectors[this_kind] - if injector.plugin_name is None: - pytest.skip('Use of inventory plugin is not enabled for this source') src_vars = dict(base_source_var='value_of_var') src_vars['plugin'] = injector.get_proper_name() @@ -200,7 +208,7 @@ def test_inventory_update_injected_content(this_kind, inventory, fake_credential source_vars=src_vars, ) inventory_source.credentials.add(fake_credential_factory(this_kind)) - inventory_update = inventory_source.create_unified_job() + inventory_update = inventory_source.create_unified_job(_eager_fields={'status': 'waiting'}) task = RunInventoryUpdate() def substitute_run(awx_receptor_job): @@ -214,6 +222,10 @@ def substitute_run(awx_receptor_job): private_data_dir = envvars.pop('AWX_PRIVATE_DATA_DIR') assert envvars.pop('ANSIBLE_INVENTORY_ENABLED') == 'auto' set_files = bool(os.getenv("MAKE_INVENTORY_REFERENCE_FILES", 'false').lower()[0] not in ['f', '0']) + + # Ensure the directory exists before trying to list/read it + os.makedirs(private_data_dir, exist_ok=True) + env, content = read_content(private_data_dir, envvars, inventory_update) # Assert inventory plugin inventory file is in private_data_dir @@ -222,7 +234,7 @@ def substitute_run(awx_receptor_job): len([True for k in content.keys() if k.endswith(inventory_filename)]) > 0 ), f"'{inventory_filename}' file not found in inventory update runtime files {content.keys()}" - env.pop('ANSIBLE_COLLECTIONS_PATHS', None) # collection paths not relevant to this test + env.pop('ANSIBLE_COLLECTIONS_PATH', None) base_dir = os.path.join(DATA, 'plugins') if not os.path.exists(base_dir): os.mkdir(base_dir) @@ -234,7 +246,7 @@ def substitute_run(awx_receptor_job): source_dir = os.path.join(base_dir, this_kind) # this_kind is a global if not os.path.exists(source_dir): - raise FileNotFoundError('Maybe you never made reference files? ' 'MAKE_INVENTORY_REFERENCE_FILES=true py.test ...\noriginal: {}') + raise FileNotFoundError('Maybe you never made reference files? MAKE_INVENTORY_REFERENCE_FILES=true py.test ...\noriginal: {}') files_dir = os.path.join(source_dir, 'files') try: expected_file_list = os.listdir(files_dir) diff --git a/awx/main/tests/functional/test_jobs.py b/awx/main/tests/functional/test_jobs.py index da3b9fd57cd3..8a550449a964 100644 --- a/awx/main/tests/functional/test_jobs.py +++ b/awx/main/tests/functional/test_jobs.py @@ -6,7 +6,9 @@ from awx.main.models import ( Job, Instance, + Host, JobHostSummary, + Inventory, InventoryUpdate, InventorySource, Project, @@ -16,11 +18,60 @@ InstanceGroup, Label, ExecutionEnvironment, + Credential, + CredentialType, + CredentialInputSource, + Organization, + JobTemplate, ) +from awx.main.tasks import jobs from awx.main.tasks.system import cluster_node_heartbeat +from awx.main.utils.db import bulk_update_sorted_by_id +from ansible_base.lib.testing.util import feature_flag_enabled, feature_flag_disabled + +from django.db import OperationalError from django.test.utils import override_settings +@pytest.fixture +def job_template_with_credentials(): + """ + Factory fixture that creates a job template with specified credentials. + + Usage: + job = job_template_with_credentials(ssh_cred, vault_cred) + """ + + def _create_job_template( + *credentials, org_name='test-org', project_name='test-project', inventory_name='test-inventory', jt_name='test-jt', playbook='test.yml' + ): + """ + Create a job template with the given credentials. + + Args: + *credentials: Variable number of Credential objects to attach to the job template + org_name: Name for the organization + project_name: Name for the project + inventory_name: Name for the inventory + jt_name: Name for the job template + playbook: Playbook filename + + Returns: + Job instance created from the job template + """ + org = Organization.objects.create(name=org_name) + proj = Project.objects.create(name=project_name, organization=org) + inv = Inventory.objects.create(name=inventory_name, organization=org) + jt = JobTemplate.objects.create(name=jt_name, project=proj, inventory=inv, playbook=playbook) + + if credentials: + jt.credentials.add(*credentials) + + return jt.create_unified_job() + + return _create_job_template + + @pytest.mark.django_db def test_orphan_unified_job_creation(instance, inventory): job = Job.objects.create(job_template=None, inventory=inventory, name='hi world') @@ -33,9 +84,9 @@ def test_orphan_unified_job_creation(instance, inventory): @pytest.mark.django_db -@mock.patch('awx.main.tasks.system.inspect_execution_nodes', lambda *args, **kwargs: None) -@mock.patch('awx.main.models.ha.get_cpu_effective_capacity', lambda cpu: 8) -@mock.patch('awx.main.models.ha.get_mem_effective_capacity', lambda mem: 62) +@mock.patch('awx.main.tasks.system.inspect_execution_and_hop_nodes', lambda *args, **kwargs: None) +@mock.patch('awx.main.models.ha.get_cpu_effective_capacity', lambda cpu, is_control_node: 8) +@mock.patch('awx.main.models.ha.get_mem_effective_capacity', lambda mem, is_control_node: 62) def test_job_capacity_and_with_inactive_node(): i = Instance.objects.create(hostname='test-1') i.save_health_data('18.0.1', 2, 8000) @@ -46,7 +97,7 @@ def test_job_capacity_and_with_inactive_node(): i.save() with override_settings(CLUSTER_HOST_ID=i.hostname): with mock.patch.object(redis.client.Redis, 'ping', lambda self: True): - cluster_node_heartbeat() + cluster_node_heartbeat(None) i = Instance.objects.get(id=i.id) assert i.capacity == 0 @@ -112,6 +163,63 @@ def test_job_notification_host_data(inventory, machine_credential, project, job_ } +@pytest.mark.django_db +class TestAnsibleFactsSave: + current_call = 0 + + def test_update_hosts_deleted_host(self, inventory): + hosts = [Host.objects.create(inventory=inventory, name=f'foo{i}') for i in range(3)] + for host in hosts: + host.ansible_facts = {'foo': 'bar'} + last_pk = hosts[-1].pk + assert inventory.hosts.count() == 3 + Host.objects.get(pk=last_pk).delete() + assert inventory.hosts.count() == 2 + bulk_update_sorted_by_id(Host, hosts, fields=['ansible_facts']) + assert inventory.hosts.count() == 2 + for host in inventory.hosts.all(): + host.refresh_from_db() + assert host.ansible_facts == {'foo': 'bar'} + + def test_update_hosts_forever_deadlock(self, inventory, mocker): + hosts = [Host.objects.create(inventory=inventory, name=f'foo{i}') for i in range(3)] + for host in hosts: + host.ansible_facts = {'foo': 'bar'} + db_mock = mocker.patch('awx.main.tasks.facts.Host.objects.bulk_update') + db_mock.side_effect = OperationalError('deadlock detected') + with pytest.raises(OperationalError): + bulk_update_sorted_by_id(Host, hosts, fields=['ansible_facts']) + + def fake_bulk_update(self, host_list): + if self.current_call > 2: + return Host.objects.bulk_update(host_list, ['ansible_facts', 'ansible_facts_modified']) + self.current_call += 1 + raise OperationalError('deadlock detected') + + +@pytest.mark.django_db +def test_update_hosts_resolved_deadlock(inventory, mocker): + + hosts = [Host.objects.create(inventory=inventory, name=f'foo{i}') for i in range(3)] + + # Set ansible_facts for each host + for host in hosts: + host.ansible_facts = {'foo': 'bar'} + + bulk_update_sorted_by_id(Host, hosts, fields=['ansible_facts']) + + # Save changes and refresh from DB to ensure the updated facts are saved + for host in hosts: + host.save() # Ensure changes are persisted in the DB + host.refresh_from_db() # Refresh from DB to get latest data + + # Assert that the ansible_facts were updated correctly + for host in inventory.hosts.all(): + assert host.ansible_facts == {'foo': 'bar'} + + bulk_update_sorted_by_id(Host, hosts, fields=['ansible_facts']) + + @pytest.mark.django_db class TestLaunchConfig: def test_null_creation_from_prompts(self): @@ -201,3 +309,442 @@ def test_pk_field(self, job_template, organization): assert config.execution_environment # We just write the PK instead of trying to assign an item, that happens on the save assert config.execution_environment_id == ee.id + + +@pytest.mark.django_db +def test_base_task_credentials_property(job_template_with_credentials): + """Test that _credentials property caches credentials and doesn't re-query.""" + task = jobs.RunJob() + + # Create real credentials + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + vault_type = CredentialType.defaults['vault']() + vault_type.save() + + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + vault_cred = Credential.objects.create(credential_type=vault_type, name='vault-cred') + + # Create a job with credentials using fixture + job = job_template_with_credentials(ssh_cred, vault_cred) + task.instance = job + + # First access should build credentials + result1 = task._credentials + assert len(result1) == 2 + assert isinstance(result1, list) + + # Second access should return cached value (we can verify by checking it's the same list object) + result2 = task._credentials + assert result2 is result1 # Same object reference + + +@pytest.mark.django_db +def test_run_job_machine_credential(job_template_with_credentials): + """Test _machine_credential returns ssh credential from cache.""" + task = jobs.RunJob() + + # Create credentials + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + vault_type = CredentialType.defaults['vault']() + vault_type.save() + + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + vault_cred = Credential.objects.create(credential_type=vault_type, name='vault-cred') + + # Create a job using fixture + job = job_template_with_credentials(ssh_cred, vault_cred) + task.instance = job + + # Set cached credentials + task._credentials = [ssh_cred, vault_cred] + + # Get machine credential + result = task._machine_credential + assert result == ssh_cred + assert result.credential_type.kind == 'ssh' + + +@pytest.mark.django_db +def test_run_job_machine_credential_none(job_template_with_credentials): + """Test _machine_credential returns None when no ssh credential exists.""" + task = jobs.RunJob() + + # Create only vault credential + vault_type = CredentialType.defaults['vault']() + vault_type.save() + vault_cred = Credential.objects.create(credential_type=vault_type, name='vault-cred') + + job = job_template_with_credentials(vault_cred) + task.instance = job + + # Set cached credentials + task._credentials = [vault_cred] + + # Get machine credential + result = task._machine_credential + assert result is None + + +@pytest.mark.django_db +def test_run_job_vault_credentials(job_template_with_credentials): + """Test _vault_credentials returns all vault credentials from cache.""" + task = jobs.RunJob() + + # Create credentials + vault_type = CredentialType.defaults['vault']() + vault_type.save() + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + vault_cred1 = Credential.objects.create(credential_type=vault_type, name='vault-1') + vault_cred2 = Credential.objects.create(credential_type=vault_type, name='vault-2') + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + + job = job_template_with_credentials(vault_cred1, ssh_cred, vault_cred2) + task.instance = job + + # Set cached credentials + task._credentials = [vault_cred1, ssh_cred, vault_cred2] + + # Get vault credentials + result = task._vault_credentials + assert len(result) == 2 + assert vault_cred1 in result + assert vault_cred2 in result + assert ssh_cred not in result + + +@pytest.mark.django_db +def test_run_job_network_credentials(job_template_with_credentials): + """Test _network_credentials returns all network credentials from cache.""" + task = jobs.RunJob() + + # Create credentials + net_type = CredentialType.defaults['net']() + net_type.save() + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + net_cred = Credential.objects.create(credential_type=net_type, name='net-cred') + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + + job = job_template_with_credentials(net_cred, ssh_cred) + task.instance = job + + # Set cached credentials + task._credentials = [net_cred, ssh_cred] + + # Get network credentials + result = task._network_credentials + assert len(result) == 1 + assert result[0] == net_cred + + +@pytest.mark.django_db +def test_run_job_cloud_credentials(job_template_with_credentials): + """Test _cloud_credentials returns all cloud credentials from cache.""" + task = jobs.RunJob() + + # Create credentials + aws_type = CredentialType.defaults['aws']() + aws_type.save() + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + aws_cred = Credential.objects.create(credential_type=aws_type, name='aws-cred') + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + + job = job_template_with_credentials(aws_cred, ssh_cred) + task.instance = job + + # Set cached credentials + task._credentials = [aws_cred, ssh_cred] + + # Get cloud credentials + result = task._cloud_credentials + assert len(result) == 1 + assert result[0] == aws_cred + + +@pytest.mark.django_db +@override_settings(RESOURCE_SERVER={'URL': 'https://gateway.example.com', 'SECRET_KEY': 'test-secret-key', 'VALIDATE_HTTPS': False}) +def test_populate_workload_identity_tokens_with_flag_enabled(job_template_with_credentials, mocker): + """Test populate_workload_identity_tokens sets context when flag is enabled.""" + with feature_flag_enabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + task = jobs.RunJob() + + # Create credential types + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + # Create a workload identity credential type + hashivault_type = CredentialType( + name='HashiCorp Vault Secret Lookup (OIDC)', + kind='cloud', + managed=False, + inputs={ + 'fields': [ + {'id': 'jwt_aud', 'type': 'string', 'label': 'JWT Audience'}, + {'id': 'workload_identity_token', 'type': 'string', 'label': 'Workload Identity Token', 'secret': True, 'internal': True}, + ] + }, + ) + hashivault_type.save() + + # Create credentials + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + source_cred = Credential.objects.create(credential_type=hashivault_type, name='vault-source', inputs={'jwt_aud': 'https://vault.example.com'}) + target_cred = Credential.objects.create(credential_type=ssh_type, name='target-cred', inputs={'username': 'testuser'}) + + # Create input source linking source credential to target credential + input_source = CredentialInputSource.objects.create( + target_credential=target_cred, source_credential=source_cred, input_field_name='password', metadata={'path': 'secret/data/password'} + ) + + # Create a job using fixture + job = job_template_with_credentials(target_cred, ssh_cred) + task.instance = job + + # Override cached_property so the loop uses these exact Python objects + task._credentials = [target_cred, ssh_cred] + + # Mock only the HTTP response from the Gateway workload identity endpoint + mock_response = mocker.Mock(status_code=200) + mock_response.json.return_value = {'jwt': 'eyJ.test.jwt'} + + mock_request = mocker.patch('requests.request', return_value=mock_response, autospec=True) + + task.populate_workload_identity_tokens() + + # Verify the HTTP call was made to the correct endpoint + mock_request.assert_called_once() + call_kwargs = mock_request.call_args.kwargs + assert call_kwargs['method'] == 'POST' + assert '/api/gateway/v1/workload_identity_tokens' in call_kwargs['url'] + + # Verify context was set on the credential, keyed by input source PK + assert input_source.pk in target_cred.context + assert target_cred.context[input_source.pk]['workload_identity_token'] == 'eyJ.test.jwt' + + +@pytest.mark.django_db +@override_settings(RESOURCE_SERVER={'URL': 'https://gateway.example.com', 'SECRET_KEY': 'test-secret-key', 'VALIDATE_HTTPS': False}) +def test_populate_workload_identity_tokens_passes_workload_ttl_from_job_timeout(job_template_with_credentials, mocker): + """Test populate_workload_identity_tokens passes workload_ttl_seconds from get_instance_timeout to the client.""" + with feature_flag_enabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + task = jobs.RunJob() + + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + hashivault_type = CredentialType( + name='HashiCorp Vault Secret Lookup (OIDC)', + kind='cloud', + managed=False, + inputs={ + 'fields': [ + {'id': 'jwt_aud', 'type': 'string', 'label': 'JWT Audience'}, + {'id': 'workload_identity_token', 'type': 'string', 'label': 'Workload Identity Token', 'secret': True, 'internal': True}, + ] + }, + ) + hashivault_type.save() + + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + source_cred = Credential.objects.create(credential_type=hashivault_type, name='vault-source', inputs={'jwt_aud': 'https://vault.example.com'}) + target_cred = Credential.objects.create(credential_type=ssh_type, name='target-cred', inputs={'username': 'testuser'}) + + CredentialInputSource.objects.create( + target_credential=target_cred, source_credential=source_cred, input_field_name='password', metadata={'path': 'secret/data/password'} + ) + + job = job_template_with_credentials(target_cred, ssh_cred) + job.timeout = 3600 + job.save() + task.instance = job + task._credentials = [target_cred, ssh_cred] + + mock_response = mocker.Mock(status_code=200) + mock_response.json.return_value = {'jwt': 'eyJ.test.jwt'} + mock_request = mocker.patch('requests.request', return_value=mock_response, autospec=True) + + task.populate_workload_identity_tokens() + + call_kwargs = mock_request.call_args.kwargs + assert call_kwargs['method'] == 'POST' + json_body = call_kwargs.get('json', {}) + assert json_body.get('workload_ttl_seconds') == 3600 + + +@pytest.mark.django_db +def test_populate_workload_identity_tokens_with_flag_disabled(job_template_with_credentials): + """Test populate_workload_identity_tokens sets error status when flag is disabled.""" + with feature_flag_disabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + task = jobs.RunJob() + + # Create credential types + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + # Create a workload identity credential type + hashivault_type = CredentialType( + name='HashiCorp Vault Secret Lookup (OIDC)', + kind='cloud', + managed=False, + inputs={ + 'fields': [ + {'id': 'jwt_aud', 'type': 'string', 'label': 'JWT Audience'}, + {'id': 'workload_identity_token', 'type': 'string', 'label': 'Workload Identity Token', 'secret': True, 'internal': True}, + ] + }, + ) + hashivault_type.save() + + # Create credentials + source_cred = Credential.objects.create(credential_type=hashivault_type, name='vault-source') + target_cred = Credential.objects.create(credential_type=ssh_type, name='target-cred', inputs={'username': 'testuser'}) + + # Create input source linking source credential to target credential + # Note: Creates the relationship that will trigger the feature flag check + CredentialInputSource.objects.create( + target_credential=target_cred, source_credential=source_cred, input_field_name='password', metadata={'path': 'secret/data/password'} + ) + + # Create a job using fixture + job = job_template_with_credentials(target_cred) + task.instance = job + + # Set cached credentials + task._credentials = [target_cred] + + task.populate_workload_identity_tokens() + + # Verify job status was set to error + job.refresh_from_db() + assert job.status == 'error' + assert 'FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED' in job.job_explanation + assert 'vault-source' in job.job_explanation + + +@pytest.mark.django_db +@override_settings(RESOURCE_SERVER={'URL': 'https://gateway.example.com', 'SECRET_KEY': 'test-secret-key', 'VALIDATE_HTTPS': False}) +def test_populate_workload_identity_tokens_multiple_input_sources_per_credential(job_template_with_credentials, mocker): + """Test that a single credential with two input sources from different workload identity + credential types gets a separate JWT token for each input source, keyed by input source PK.""" + with feature_flag_enabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + task = jobs.RunJob() + + # Create credential types + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + + # Create two different workload identity credential types + hashivault_kv_type = CredentialType( + name='HashiCorp Vault Secret Lookup (OIDC)', + kind='cloud', + managed=False, + inputs={ + 'fields': [ + {'id': 'jwt_aud', 'type': 'string', 'label': 'JWT Audience'}, + {'id': 'workload_identity_token', 'type': 'string', 'label': 'Workload Identity Token', 'secret': True, 'internal': True}, + ] + }, + ) + hashivault_kv_type.save() + + hashivault_ssh_type = CredentialType( + name='HashiCorp Vault Signed SSH (OIDC)', + kind='cloud', + managed=False, + inputs={ + 'fields': [ + {'id': 'jwt_aud', 'type': 'string', 'label': 'JWT Audience'}, + {'id': 'workload_identity_token', 'type': 'string', 'label': 'Workload Identity Token', 'secret': True, 'internal': True}, + ] + }, + ) + hashivault_ssh_type.save() + + # Create source credentials with different audiences + source_cred_kv = Credential.objects.create( + credential_type=hashivault_kv_type, name='vault-kv-source', inputs={'jwt_aud': 'https://vault-kv.example.com'} + ) + source_cred_ssh = Credential.objects.create( + credential_type=hashivault_ssh_type, name='vault-ssh-source', inputs={'jwt_aud': 'https://vault-ssh.example.com'} + ) + + # Create target credential that uses both sources for different fields + target_cred = Credential.objects.create(credential_type=ssh_type, name='target-cred', inputs={'username': 'testuser'}) + + # Create two input sources on the same target credential, each for a different field + input_source_password = CredentialInputSource.objects.create( + target_credential=target_cred, source_credential=source_cred_kv, input_field_name='password', metadata={'path': 'secret/data/password'} + ) + input_source_ssh_key = CredentialInputSource.objects.create( + target_credential=target_cred, source_credential=source_cred_ssh, input_field_name='ssh_key_data', metadata={'path': 'secret/data/ssh_key'} + ) + + # Create a job using fixture + job = job_template_with_credentials(target_cred) + task.instance = job + + # Override cached_property so the loop uses this exact Python object + task._credentials = [target_cred] + + # Mock HTTP responses - return different JWTs for each call + response_kv = mocker.Mock(status_code=200) + response_kv.json.return_value = {'jwt': 'eyJ.kv.jwt'} + + response_ssh = mocker.Mock(status_code=200) + response_ssh.json.return_value = {'jwt': 'eyJ.ssh.jwt'} + + mock_request = mocker.patch('requests.request', side_effect=[response_kv, response_ssh], autospec=True) + + task.populate_workload_identity_tokens() + + # Verify two separate HTTP calls were made (one per input source) + assert mock_request.call_count == 2 + + # Verify each call used the correct audience from its source credential + audiences_requested = {call.kwargs.get('json', {}).get('audience', '') for call in mock_request.call_args_list} + assert 'https://vault-kv.example.com' in audiences_requested + assert 'https://vault-ssh.example.com' in audiences_requested + + # Verify context on the target credential has both tokens, keyed by input source PK + assert input_source_password.pk in target_cred.context + assert input_source_ssh_key.pk in target_cred.context + assert target_cred.context[input_source_password.pk]['workload_identity_token'] == 'eyJ.kv.jwt' + assert target_cred.context[input_source_ssh_key.pk]['workload_identity_token'] == 'eyJ.ssh.jwt' + + +@pytest.mark.django_db +def test_populate_workload_identity_tokens_without_workload_identity_credentials(job_template_with_credentials, mocker): + """Test populate_workload_identity_tokens does nothing when no workload identity credentials.""" + with feature_flag_enabled('FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED'): + task = jobs.RunJob() + + # Create only standard credentials (no workload identity) + ssh_type = CredentialType.defaults['ssh']() + ssh_type.save() + vault_type = CredentialType.defaults['vault']() + vault_type.save() + + ssh_cred = Credential.objects.create(credential_type=ssh_type, name='ssh-cred') + vault_cred = Credential.objects.create(credential_type=vault_type, name='vault-cred') + + # Create a job using fixture + job = job_template_with_credentials(ssh_cred, vault_cred) + task.instance = job + + # Set cached credentials + task._credentials = [ssh_cred, vault_cred] + + mocker.patch('awx.main.tasks.jobs.populate_claims_for_workload', return_value={'job_id': 123}, autospec=True) + + task.populate_workload_identity_tokens() + + # Verify no context was set + assert not hasattr(ssh_cred, '_context') or ssh_cred.context == {} + assert not hasattr(vault_cred, '_context') or vault_cred.context == {} diff --git a/awx/main/tests/functional/test_ldap.py b/awx/main/tests/functional/test_ldap.py deleted file mode 100644 index 2467ff52e38f..000000000000 --- a/awx/main/tests/functional/test_ldap.py +++ /dev/null @@ -1,103 +0,0 @@ -import ldap -import ldif -import pytest -import os -from mockldap import MockLdap - -from awx.api.versioning import reverse - - -@pytest.fixture -def ldap_generator(): - def fn(fname, host='localhost'): - fh = open(os.path.join(os.path.dirname(os.path.realpath(__file__)), fname), 'rb') - ctrl = ldif.LDIFRecordList(fh) - ctrl.parse() - - directory = dict(ctrl.all_records) - - mockldap = MockLdap(directory) - - mockldap.start() - mockldap['ldap://{}/'.format(host)] - - conn = ldap.initialize('ldap://{}/'.format(host)) - - return conn - # mockldap.stop() - - return fn - - -@pytest.fixture -def ldap_settings_generator(): - def fn(prefix='', dc='ansible', host='ldap.ansible.com'): - prefix = '_{}'.format(prefix) if prefix else '' - - data = { - 'AUTH_LDAP_SERVER_URI': 'ldap://{}'.format(host), - 'AUTH_LDAP_BIND_DN': 'cn=eng_user1,ou=people,dc={},dc=com'.format(dc), - 'AUTH_LDAP_BIND_PASSWORD': 'password', - "AUTH_LDAP_USER_SEARCH": ["ou=people,dc={},dc=com".format(dc), "SCOPE_SUBTREE", "(cn=%(user)s)"], - "AUTH_LDAP_TEAM_MAP": { - "LDAP Sales": {"organization": "LDAP Organization", "users": "cn=sales,ou=groups,dc={},dc=com".format(dc), "remove": True}, - "LDAP IT": {"organization": "LDAP Organization", "users": "cn=it,ou=groups,dc={},dc=com".format(dc), "remove": True}, - "LDAP Engineering": {"organization": "LDAP Organization", "users": "cn=engineering,ou=groups,dc={},dc=com".format(dc), "remove": True}, - }, - "AUTH_LDAP_REQUIRE_GROUP": None, - "AUTH_LDAP_USER_ATTR_MAP": {"first_name": "givenName", "last_name": "sn", "email": "mail"}, - "AUTH_LDAP_GROUP_SEARCH": ["dc={},dc=com".format(dc), "SCOPE_SUBTREE", "(objectClass=groupOfNames)"], - "AUTH_LDAP_USER_FLAGS_BY_GROUP": {"is_superuser": "cn=superusers,ou=groups,dc={},dc=com".format(dc)}, - "AUTH_LDAP_ORGANIZATION_MAP": { - "LDAP Organization": { - "admins": "cn=engineering_admins,ou=groups,dc={},dc=com".format(dc), - "remove_admins": False, - "users": [ - "cn=engineering,ou=groups,dc={},dc=com".format(dc), - "cn=sales,ou=groups,dc={},dc=com".format(dc), - "cn=it,ou=groups,dc={},dc=com".format(dc), - ], - "remove_users": False, - } - }, - } - - if prefix: - data_new = dict() - for k, v in data.items(): - k_new = k.replace('AUTH_LDAP', 'AUTH_LDAP{}'.format(prefix)) - data_new[k_new] = v - else: - data_new = data - - return data_new - - return fn - - -# Note: mockldap isn't fully featured. Fancy queries aren't fully baked. -# However, objects returned are solid so they should flow through django ldap middleware nicely. -@pytest.mark.skip(reason="Needs Update - CA") -@pytest.mark.django_db -def test_login(ldap_generator, patch, post, admin, ldap_settings_generator): - auth_url = reverse('api:auth_token_view') - ldap_settings_url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ldap'}) - - # Generate mock ldap servers and init with ldap data - ldap_generator("../data/ldap_example.ldif", "ldap.example.com") - ldap_generator("../data/ldap_redhat.ldif", "ldap.redhat.com") - ldap_generator("../data/ldap_ansible.ldif", "ldap.ansible.com") - - ldap_settings_example = ldap_settings_generator(dc='example') - ldap_settings_ansible = ldap_settings_generator(prefix='1', dc='ansible') - ldap_settings_redhat = ldap_settings_generator(prefix='2', dc='redhat') - - # eng_user1 exists in ansible and redhat but not example - patch(ldap_settings_url, user=admin, data=ldap_settings_example, expect=200) - - post(auth_url, data={'username': 'eng_user1', 'password': 'password'}, expect=400) - - patch(ldap_settings_url, user=admin, data=ldap_settings_ansible, expect=200) - patch(ldap_settings_url, user=admin, data=ldap_settings_redhat, expect=200) - - post(auth_url, data={'username': 'eng_user1', 'password': 'password'}, expect=200) diff --git a/awx/main/tests/functional/test_licenses.py b/awx/main/tests/functional/test_licenses.py index 4b8ee7a9189a..aefec6153b3c 100644 --- a/awx/main/tests/functional/test_licenses.py +++ b/awx/main/tests/functional/test_licenses.py @@ -1,5 +1,4 @@ import glob -import json import os from django.conf import settings @@ -12,122 +11,99 @@ from pip._internal.req.constructors import parse_req_from_line -def test_python_and_js_licenses(): - def index_licenses(path): - # Check for GPL (forbidden) and LGPL (need to ship source) - # This is not meant to be an exhaustive check. - def check_license(license_file): - with open(license_file) as f: - data = f.read() - is_lgpl = 'GNU LESSER GENERAL PUBLIC LICENSE' in data.upper() - # The LGPL refers to the GPL in-text - # Case-sensitive for GPL to match license text and not PSF license reference - is_gpl = 'GNU GENERAL PUBLIC LICENSE' in data and not is_lgpl - return (is_gpl, is_lgpl) - - def find_embedded_source_version(path, name): - for entry in os.listdir(path): - # Check variations of '-' and '_' in filenames due to python - for fname in [name, name.replace('-', '_')]: - if entry.startswith(fname) and entry.endswith('.tar.gz'): - v = entry.split(name + '-')[1].split('.tar.gz')[0] - return v - return None - - list = {} - for txt_file in glob.glob('%s/*.txt' % path): - filename = txt_file.split('/')[-1] - name = filename[:-4].lower() - (is_gpl, is_lgpl) = check_license(txt_file) - list[name] = { - 'name': name, - 'filename': filename, - 'gpl': is_gpl, - 'source_required': (is_gpl or is_lgpl), - 'source_version': find_embedded_source_version(path, name), - } - return list - - def read_api_requirements(path): - ret = {} - skip_pbr_license_check = False - for req_file in ['requirements.txt', 'requirements_git.txt']: - fname = '%s/%s' % (path, req_file) - - for reqt in parse_requirements(fname, session=''): - parsed_requirement = parse_req_from_line(reqt.requirement, None) - name = parsed_requirement.requirement.name - version = str(parsed_requirement.requirement.specifier) - if version.startswith('=='): - version = version[2:] - if parsed_requirement.link: - if str(parsed_requirement.link).startswith(('http://', 'https://')): - (name, version) = str(parsed_requirement.requirement).split('==', 1) - else: - (name, version) = parsed_requirement.link.filename.split('@', 1) - if name.endswith('.git'): - name = name[:-4] - if name == 'receptor': - name = 'receptorctl' - if name == 'ansible-runner': - skip_pbr_license_check = True - ret[name] = {'name': name, 'version': version} - if 'pbr' in ret and skip_pbr_license_check: - del ret['pbr'] - return ret - - def read_ui_requirements(path): - def json_deps(jsondata): - ret = {} - deps = jsondata.get('dependencies', {}) - for key in deps.keys(): - key = key.lower() - devonly = deps[key].get('dev', False) - if not devonly: - if key not in ret.keys(): - depname = key.replace('/', '-') - if depname[0] == '@': - depname = depname[1:] - ret[depname] = {'name': depname, 'version': deps[key]['version']} - ret.update(json_deps(deps[key])) - return ret - - with open('%s/package-lock.json' % path) as f: - jsondata = json.load(f) - return json_deps(jsondata) - - def remediate_licenses_and_requirements(licenses, requirements): - errors = [] - items = list(licenses.keys()) - items.sort() - for item in items: - if item not in [r.lower() for r in requirements.keys()] and item != 'awx': - errors.append(" license file %s does not correspond to an existing requirement; it should be removed." % (licenses[item]['filename'],)) - continue - # uWSGI has a linking exception - if licenses[item]['gpl'] and item != 'uwsgi': - errors.append(" license for %s is GPL. This software cannot be used." % (item,)) - if licenses[item]['source_required']: - version = requirements[item]['version'] - if version != licenses[item]['source_version']: - errors.append(" embedded source for %s is %s instead of the required version %s" % (item, licenses[item]['source_version'], version)) - elif licenses[item]['source_version']: - errors.append(" embedded source version %s for %s is included despite not being needed" % (licenses[item]['source_version'], item)) - items = list(requirements.keys()) - items.sort() - for item in items: - if item.lower() not in licenses.keys(): - errors.append(" license for requirement %s is missing" % (item,)) - return errors +def check_license(license_file): + with open(license_file) as f: + data = f.read() + is_lgpl = 'GNU LESSER GENERAL PUBLIC LICENSE' in data.upper() + is_gpl = 'GNU GENERAL PUBLIC LICENSE' in data and not is_lgpl + return is_gpl, is_lgpl + + +def find_embedded_source_version(path, name): + files = os.listdir(path) + tgz_files = [f for f in files if f.endswith('.tar.gz')] + for tgz in tgz_files: + pkg_name = tgz.split('-')[0].split('_')[0] + if pkg_name == name: + return tgz.split('-')[1].split('.tar.gz')[0] + return None + + +def index_licenses(path): + licenses = {} + for txt_file in glob.glob(f'{path}/*.txt'): + filename = os.path.basename(txt_file) + name = filename[:-4].lower() + is_gpl, is_lgpl = check_license(txt_file) + licenses[name] = { + 'name': name, + 'filename': filename, + 'gpl': is_gpl, + 'source_required': is_gpl or is_lgpl, + 'source_version': find_embedded_source_version(path, name), + } + return licenses + + +def parse_requirement(reqt): + parsed_requirement = parse_req_from_line(reqt.requirement, None) + assert parsed_requirement.requirement, reqt.__dict__ + name = parsed_requirement.requirement.name + version = str(parsed_requirement.requirement.specifier) + if version.startswith('=='): + version = version[2:] + if parsed_requirement.link: + if str(parsed_requirement.link).startswith(('http://', 'https://')): + name, version = str(parsed_requirement.requirement).split('==', 1) + else: + name, version = parsed_requirement.link.filename.split('@', 1) + if name.endswith('.git'): + name = name[:-4] + if name == 'receptor': + name = 'receptorctl' + return name, version - base_dir = settings.BASE_DIR - api_licenses = index_licenses('%s/../licenses' % base_dir) - ui_licenses = index_licenses('%s/../licenses/ui' % base_dir) - api_requirements = read_api_requirements('%s/../requirements' % base_dir) - ui_requirements = read_ui_requirements('%s/ui' % base_dir) +def read_api_requirements(path): + requirements = {} + skip_pbr_license_check = False + for req_file in ['requirements.txt', 'requirements_git.txt']: + fname = f'{path}/{req_file}' + for reqt in parse_requirements(fname, session=''): + name, version = parse_requirement(reqt) + if name == 'ansible-runner': + skip_pbr_license_check = True + requirements[name] = {'name': name, 'version': version} + if 'pbr' in requirements and skip_pbr_license_check: + del requirements['pbr'] + return requirements + + +def remediate_licenses_and_requirements(licenses, requirements): errors = [] - errors += remediate_licenses_and_requirements(ui_licenses, ui_requirements) - errors += remediate_licenses_and_requirements(api_licenses, api_requirements) + for item in sorted(licenses.keys()): + if item not in [r.lower() for r in requirements.keys()] and item != 'awx': + errors.append(f" license file {licenses[item]['filename']} does not correspond to an existing requirement; it should be removed.") + continue + if licenses[item]['gpl'] and item != 'uwsgi': + errors.append(f" license for {item} is GPL. This software cannot be used.") + if licenses[item]['source_required']: + version = requirements[item]['version'] + if version != licenses[item]['source_version']: + errors.append(f" embedded source for {item} is {licenses[item]['source_version']} instead of the required version {version}") + elif licenses[item]['source_version']: + errors.append(f" embedded source version {licenses[item]['source_version']} for {item} is included despite not being needed") + for item in sorted(requirements.keys()): + if item.lower() not in licenses.keys(): + errors.append(f" license for requirement {item} is missing") + return errors + + +def test_python_licenses(): + base_dir = settings.BASE_DIR + api_licenses = index_licenses(f'{base_dir}/../licenses') + api_requirements = read_api_requirements(f'{base_dir}/../requirements') + + errors = remediate_licenses_and_requirements(api_licenses, api_requirements) if errors: raise Exception('Included licenses not consistent with requirements:\n%s' % '\n'.join(errors)) diff --git a/awx/main/tests/functional/test_migrations.py b/awx/main/tests/functional/test_migrations.py new file mode 100644 index 000000000000..caa9579f6abb --- /dev/null +++ b/awx/main/tests/functional/test_migrations.py @@ -0,0 +1,226 @@ +import pytest + +from django_test_migrations.plan import all_migrations, nodes_to_tuples +from django.utils.timezone import now + +""" +Most tests that live in here can probably be deleted at some point. They are mainly +for a developer. When AWX versions that users upgrade from falls out of support that +is when migration tests can be deleted. This is also a good time to squash. Squashing +will likely mess with the tests that live here. +The smoke test should be kept in here. The smoke test ensures that our migrations +continue to work when sqlite is the backing database (vs. the default DB of postgres). +""" + + +@pytest.mark.django_db +class TestMigrationSmoke: + def test_happy_path(self, migrator): + """ + This smoke test runs all the migrations. + Example of how to use django-test-migration to invoke particular migration(s) + while weaving in object creation and assertions. + Note that this is more than just an example. It is a smoke test because it runs ALL + the migrations. Our "normal" unit tests subvert the migrations running because it is slow. + """ + migration_nodes = all_migrations('default') + migration_tuples = nodes_to_tuples(migration_nodes) + final_migration = migration_tuples[-1] + migrator.apply_initial_migration(('main', None)) + # I just picked a newish migration at the time of writing this. + # If someone from the future finds themselves here because the are squashing migrations + # it is fine to change the 0180_... below to some other newish migration + intermediate_state = migrator.apply_tested_migration(('main', '0180_add_hostmetric_fields')) + Instance = intermediate_state.apps.get_model('main', 'Instance') + # Create any old object in the database + Instance.objects.create(hostname='foobar', node_type='control') + final_state = migrator.apply_tested_migration(final_migration) + Instance = final_state.apps.get_model('main', 'Instance') + assert Instance.objects.filter(hostname='foobar').count() == 1 + + def test_receptor_address(self, migrator): + old_state = migrator.apply_initial_migration(('main', '0188_add_bitbucket_dc_webhook')) + Instance = old_state.apps.get_model('main', 'Instance') + for i in range(3): + Instance.objects.create(hostname=f'foobar{i}', node_type='hop') + foo = Instance.objects.create(hostname='foo', node_type='execution', listener_port=1234) + bar = Instance.objects.create(hostname='bar', node_type='execution', listener_port=None) + bar.peers.add(foo) + new_state = migrator.apply_tested_migration( + ('main', '0189_inbound_hop_nodes'), + ) + Instance = new_state.apps.get_model('main', 'Instance') + ReceptorAddress = new_state.apps.get_model('main', 'ReceptorAddress') + # We can now test how our migration worked, new field is there: + assert ReceptorAddress.objects.filter(address='foo', port=1234).count() == 1 + assert not ReceptorAddress.objects.filter(address='bar').exists() + bar = Instance.objects.get(hostname='bar') + fooaddr = ReceptorAddress.objects.get(address='foo') + bar_peers = bar.peers.all() + assert len(bar_peers) == 1 + assert fooaddr in bar_peers + + def test_migrate_DAB_RBAC(self, migrator): + old_state = migrator.apply_initial_migration(('main', '0190_alter_inventorysource_source_and_more')) + Organization = old_state.apps.get_model('main', 'Organization') + Team = old_state.apps.get_model('main', 'Team') + User = old_state.apps.get_model('auth', 'User') + org = Organization.objects.create(name='arbitrary-org', created=now(), modified=now()) + user = User.objects.create(username='random-user') + org.read_role.members.add(user) + org.member_role.members.add(user) + + team = Team.objects.create(name='arbitrary-team', organization=org, created=now(), modified=now()) + team.member_role.members.add(user) + + new_state = migrator.apply_tested_migration( + ('main', '0192_custom_roles'), + ) + RoleUserAssignment = new_state.apps.get_model('dab_rbac', 'RoleUserAssignment') + assert RoleUserAssignment.objects.filter(user=user.id, object_id=org.id).exists() + assert RoleUserAssignment.objects.filter(user=user.id, role_definition__name='Organization Member', object_id=org.id).exists() + assert RoleUserAssignment.objects.filter(user=user.id, role_definition__name='Team Member', object_id=team.id).exists() + + # Regression testing for bug that comes from current vs past models mismatch + RoleDefinition = new_state.apps.get_model('dab_rbac', 'RoleDefinition') + assert not RoleDefinition.objects.filter(name='Organization Organization Admin').exists() + # Test special cases in managed role creation + assert not RoleDefinition.objects.filter(name='Organization Team Admin').exists() + assert not RoleDefinition.objects.filter(name='Organization InstanceGroup Admin').exists() + # Test that a removed EE model permission has been deleted + new_state = migrator.apply_tested_migration( + ('main', '0195_EE_permissions'), + ) + DABPermission = new_state.apps.get_model('dab_rbac', 'DABPermission') + assert not DABPermission.objects.filter(codename='view_executionenvironment').exists() + + # Test create a Project with a duplicate name + Organization = new_state.apps.get_model('main', 'Organization') + Project = new_state.apps.get_model('main', 'Project') + WorkflowJobTemplate = new_state.apps.get_model('main', 'WorkflowJobTemplate') + org = Organization.objects.create(name='duplicate-obj-organization', created=now(), modified=now()) + proj_ids = [] + for i in range(3): + proj = Project.objects.create(name='duplicate-project-name', organization=org, created=now(), modified=now()) + proj_ids.append(proj.id) + + # Test create WorkflowJobTemplate with duplicate names + wfjt_ids = [] + for i in range(3): + wfjt = WorkflowJobTemplate.objects.create(name='duplicate-workflow-name', organization=org, created=now(), modified=now()) + wfjt_ids.append(wfjt.id) + + # The uniqueness rules will not apply to InventorySource + Inventory = new_state.apps.get_model('main', 'Inventory') + InventorySource = new_state.apps.get_model('main', 'InventorySource') + inv = Inventory.objects.create(name='migration-test-inv', organization=org, created=now(), modified=now()) + InventorySource.objects.create(name='migration-test-src', source='file', inventory=inv, organization=org, created=now(), modified=now()) + + # Apply migration 0200 which should rename duplicates + new_state = migrator.apply_tested_migration( + ('main', '0200_template_name_constraint'), + ) + + # Get the models from the new state for verification + Project = new_state.apps.get_model('main', 'Project') + WorkflowJobTemplate = new_state.apps.get_model('main', 'WorkflowJobTemplate') + InventorySource = new_state.apps.get_model('main', 'InventorySource') + + for i, proj_id in enumerate(proj_ids): + proj = Project.objects.get(id=proj_id) + if i == 0: + assert proj.name == 'duplicate-project-name' + else: + assert proj.name != 'duplicate-project-name' + assert proj.name.startswith('duplicate-project-name') + + # Verify WorkflowJobTemplate duplicates are renamed + for i, wfjt_id in enumerate(wfjt_ids): + wfjt = WorkflowJobTemplate.objects.get(id=wfjt_id) + if i == 0: + assert wfjt.name == 'duplicate-workflow-name' + else: + assert wfjt.name != 'duplicate-workflow-name' + assert wfjt.name.startswith('duplicate-workflow-name') + + # The inventory source had this field set to avoid the constrains + inv_src = InventorySource.objects.get(name='migration-test-src') + assert inv_src.org_unique is False + for proj in Project.objects.all(): + assert proj.org_unique is True + + # Piggyback test for the new credential types + validate_exists = ['GitHub App Installation Access Token Lookup', 'Terraform backend configuration'] + CredentialType = new_state.apps.get_model('main', 'CredentialType') + # simulate an upgrade by deleting existing types with these names + for expected_name in validate_exists: + ct = CredentialType.objects.filter(name=expected_name).first() + if ct: + ct.delete() + + new_state = migrator.apply_tested_migration( + ('main', '0201_create_managed_creds'), + ) + + CredentialType = new_state.apps.get_model('main', 'CredentialType') + for expected_name in validate_exists: + assert CredentialType.objects.filter( + name=expected_name + ).exists(), f'Could not find {expected_name} credential type name, all names: {list(CredentialType.objects.values_list("name", flat=True))}' + + # Verify the system_administrator role exists + Role = new_state.apps.get_model('main', 'Role') + assert Role.objects.filter( + singleton_name='system_administrator', role_field='system_administrator' + ).exists(), "expected to find a system_administrator singleton role" + + +@pytest.mark.django_db +class TestGithubAppBug: + """ + Tests that `awx-manage createsuperuser` runs successfully after + the `github_app` CredentialType kind is updated to `github_app_lookup` + via the migration. + """ + + def test_after_github_app_kind_migration(self, migrator): + """ + Verifies that `createsuperuser` does not raise a KeyError + after the 0204_squashed_deletions migration (which includes + the `update_github_app_kind` logic) is applied. + """ + # 1. Apply migrations up to the point *before* the 0204_squashed_deletions migration. + # This simulates the state where the problematic CredentialType might exist. + # We use 0203_remove_team_of_teams as the direct predecessor. + old_state = migrator.apply_tested_migration(('main', '0203_remove_team_of_teams')) + + # Get the CredentialType model from the historical state. + CredentialType = old_state.apps.get_model('main', 'CredentialType') + + # Create a CredentialType with the old, problematic 'namespace' value + CredentialType.objects.create( + name='Legacy GitHub App Credential', + kind='external', + namespace='github_app', # The namespace that causes the KeyError in the registry lookup + managed=True, + created=now(), + modified=now(), + ) + + # Apply the migration that includes the fix (0204_squashed_deletions). + new_state = migrator.apply_tested_migration(('main', '0204_squashed_deletions')) + + # Verify that the CredentialType with the old 'kind' no longer exists + # and the 'kind' has been updated to the new value. + CredentialType = new_state.apps.get_model('main', 'CredentialType') # Get CredentialType model from the new state + + # Assertion 1: The CredentialType with the old 'github_app' kind should no longer exist. + assert not CredentialType.objects.filter( + namespace='github_app' + ).exists(), "CredentialType with old 'github_app' kind should no longer exist after migration." + + # Assertion 2: The CredentialType should now exist with the new 'github_app_lookup' kind + # and retain its original name. + assert CredentialType.objects.filter( + namespace='github_app_lookup', name='Legacy GitHub App Credential' + ).exists(), "CredentialType should be updated to 'github_app_lookup' and retain its name." diff --git a/awx/main/tests/functional/test_named_url.py b/awx/main/tests/functional/test_named_url.py index 884ecd7dc0c7..5bf12653ac04 100644 --- a/awx/main/tests/functional/test_named_url.py +++ b/awx/main/tests/functional/test_named_url.py @@ -1,11 +1,6 @@ # -*- coding: utf-8 -*- -from unittest import mock - import pytest -from django.core.exceptions import ImproperlyConfigured -from django.conf import settings - from awx.api.versioning import reverse from awx.main.middleware import URLModificationMiddleware from awx.main.models import ( # noqa @@ -23,25 +18,6 @@ User, WorkflowJobTemplate, ) -from awx.conf import settings_registry - - -def setup_module(module): - # In real-world scenario, named url graph structure is populated by __init__ - # of URLModificationMiddleware. The way Django bootstraps ensures the initialization - # will happen *once and only once*, while the number of initialization is uncontrollable - # in unit test environment. So it is wrapped by try-except block to mute any - # unwanted exceptions. - try: - URLModificationMiddleware(mock.Mock()) - except ImproperlyConfigured: - pass - - -def teardown_module(module): - # settings_registry will be persistent states unless we explicitly clean them up. - settings_registry.unregister('NAMED_URL_FORMATS') - settings_registry.unregister('NAMED_URL_GRAPH_NODES') @pytest.mark.django_db @@ -143,7 +119,7 @@ def test_notification_template(get, admin_user): @pytest.mark.django_db -def test_instance(get, admin_user): +def test_instance(get, admin_user, settings): test_instance = Instance.objects.create(uuid=settings.SYSTEM_UUID, hostname="localhost", capacity=100) url = reverse('api:instance_detail', kwargs={'pk': test_instance.pk}) response = get(url, user=admin_user, expect=200) @@ -227,3 +203,76 @@ def test_403_vs_404(get): get(f'/api/v2/users/{cindy.pk}/', expect=401) get('/api/v2/users/cindy/', expect=404) + + +@pytest.mark.django_db +class TestConvertNamedUrl: + @pytest.mark.parametrize( + "url", + ( + "/api/", + "/api/v2/", + "/api/v2/hosts/", + "/api/v2/hosts/1/", + "/api/v2/organizations/1/inventories/", + "/api/foo/", + "/api/foo/v2/", + "/api/foo/v2/organizations/", + "/api/foo/v2/organizations/1/", + "/api/foo/v2/organizations/1/inventories/", + "/api/foobar/", + "/api/foobar/v2/", + "/api/foobar/v2/organizations/", + "/api/foobar/v2/organizations/1/", + "/api/foobar/v2/organizations/1/inventories/", + "/api/foobar/v2/organizations/1/inventories/", + ), + ) + def test_noop(self, url, settings): + settings.OPTIONAL_API_URLPATTERN_PREFIX = '' + assert URLModificationMiddleware._convert_named_url(url) == url + + settings.OPTIONAL_API_URLPATTERN_PREFIX = 'foo' + assert URLModificationMiddleware._convert_named_url(url) == url + + def test_named_org(self): + test_org = Organization.objects.create(name='test_org') + + assert URLModificationMiddleware._convert_named_url('/api/v2/organizations/test_org/') == f'/api/v2/organizations/{test_org.pk}/' + + def test_named_org_optional_api_urlpattern_prefix_interaction(self, settings): + settings.OPTIONAL_API_URLPATTERN_PREFIX = 'bar' + test_org = Organization.objects.create(name='test_org') + + assert URLModificationMiddleware._convert_named_url('/api/bar/v2/organizations/test_org/') == f'/api/bar/v2/organizations/{test_org.pk}/' + + @pytest.mark.parametrize("prefix", ['', 'bar']) + def test_named_org_not_found(self, prefix, settings): + settings.OPTIONAL_API_URLPATTERN_PREFIX = prefix + if prefix: + prefix += '/' + + assert URLModificationMiddleware._convert_named_url(f'/api/{prefix}v2/organizations/does-not-exist/') == f'/api/{prefix}v2/organizations/0/' + + @pytest.mark.parametrize("prefix", ['', 'bar']) + def test_named_sub_resource(self, prefix, settings): + settings.OPTIONAL_API_URLPATTERN_PREFIX = prefix + test_org = Organization.objects.create(name='test_org') + if prefix: + prefix += '/' + + assert ( + URLModificationMiddleware._convert_named_url(f'/api/{prefix}v2/organizations/test_org/inventories/') + == f'/api/{prefix}v2/organizations/{test_org.pk}/inventories/' + ) + + def test_named_job_template(self): + org = Organization.objects.create(name='test_org') + tpl = JobTemplate.objects.create(name='test_tpl', organization=org) + + # first, cause a '404' - we want to verify that no state from previous requests is carried over when named + # urls are resolved + assert URLModificationMiddleware._convert_named_url('/api/v2/job_templates/test/tpl++test_org/') == '/api/v2/job_templates/test/tpl++test_org/' + + # try to resolve a valid url - it should succeed + assert URLModificationMiddleware._convert_named_url('/api/v2/job_templates/test_tpl++test_org/') == f'/api/v2/job_templates/{tpl.pk}/' diff --git a/awx/main/tests/functional/test_notifications.py b/awx/main/tests/functional/test_notifications.py index 08036db97c90..cf93030ac6ac 100644 --- a/awx/main/tests/functional/test_notifications.py +++ b/awx/main/tests/functional/test_notifications.py @@ -16,8 +16,7 @@ @pytest.mark.django_db def test_get_notification_template_list(get, user, notification_template): url = reverse('api:notification_template_list') - response = get(url, user('admin', True)) - assert response.status_code == 200 + response = get(url, user('admin', True), expect=200) assert len(response.data['results']) == 1 @@ -35,8 +34,8 @@ def test_basic_parameterization(get, post, user, organization): notification_configuration=dict(url="http://localhost", disable_ssl_verification=False, headers={"Test": "Header"}), ), u, + expect=201, ) - assert response.status_code == 201 url = reverse('api:notification_template_detail', kwargs={'pk': response.data['id']}) response = get(url, u) assert 'related' in response.data @@ -69,8 +68,8 @@ def assert_send(self, messages): notification_configuration=dict(account_sid="dummy", account_token="shouldhide", from_number="+19999999999", to_numbers=["9998887777"]), ), u, + expect=201, ) - assert response.status_code == 201 notification_template_actual = NotificationTemplate.objects.get(id=response.data['id']) url = reverse('api:notification_template_detail', kwargs={'pk': response.data['id']}) response = get(url, u) @@ -96,8 +95,8 @@ def test_inherited_notification_templates(get, post, user, organization, project notification_configuration=dict(url="http://localhost", disable_ssl_verification=False, headers={"Test": "Header"}), ), u, + expect=201, ) - assert response.status_code == 201 notification_templates.append(response.data['id']) i = Inventory.objects.create(name='test', organization=organization) i.save() @@ -122,8 +121,7 @@ def test_disallow_delete_when_notifications_pending(delete, user, notification_t u = user('superuser', True) url = reverse('api:notification_template_detail', kwargs={'pk': notification_template.id}) Notification.objects.create(notification_template=notification_template, status='pending') - response = delete(url, user=u) - assert response.status_code == 405 + delete(url, user=u, expect=405) @pytest.mark.django_db @@ -133,9 +131,8 @@ def test_notification_template_list_includes_notification_errors(get, user, noti Notification.objects.create(notification_template=notification_template, status='successful') url = reverse('api:notification_template_list') u = user('superuser', True) - response = get(url, user=u) + response = get(url, user=u, expect=200) - assert response.status_code == 200 notifications = response.data['results'][0]['summary_fields']['recent_notifications'] assert len(notifications) == 3 statuses = [n['status'] for n in notifications] @@ -163,8 +160,8 @@ def test_custom_environment_injection(post, user, organization): notification_configuration=dict(url="https://example.org", disable_ssl_verification=False, http_method="POST", headers={"Test": "Header"}), ), u, + expect=201, ) - assert response.status_code == 201 template = NotificationTemplate.objects.get(pk=response.data['id']) with pytest.raises(ConnectionError), override_settings(AWX_TASK_ENV={'HTTPS_PROXY': '192.168.50.100:1234'}), mock.patch.object( HTTPAdapter, 'send' @@ -218,3 +215,31 @@ def test_webhook_notification_pointed_to_a_redirect_launch_endpoint(post, admin, ) assert n1.send("", n1.messages.get("success").get("body")) == 1 + + +@pytest.mark.django_db +def test_update_notification_template(admin, notification_template): + notification_template.messages['workflow_approval'] = { + "running": { + "message": None, + "body": None, + } + } + notification_template.save() + + workflow_approval_message = { + "approved": { + "message": None, + "body": None, + }, + "running": { + "message": "test-message", + "body": None, + }, + } + notification_template.messages['workflow_approval'] = workflow_approval_message + notification_template.save() + + subevents = sorted(notification_template.messages["workflow_approval"].keys()) + assert subevents == ["approved", "running"] + assert notification_template.messages['workflow_approval'] == workflow_approval_message diff --git a/awx/main/tests/functional/test_policy.py b/awx/main/tests/functional/test_policy.py new file mode 100644 index 000000000000..f265a55e6413 --- /dev/null +++ b/awx/main/tests/functional/test_policy.py @@ -0,0 +1,631 @@ +import json +import os +from unittest import mock + +import pytest +import requests.exceptions +from django.test import override_settings + +from awx.main.models import ( + Job, + Inventory, + Project, + Organization, + JobTemplate, + Credential, + CredentialType, + User, + Team, + Label, + WorkflowJob, + WorkflowJobNode, + InventorySource, +) +from awx.main.exceptions import PolicyEvaluationError +from awx.main.tasks import policy +from awx.main.tasks.policy import JobSerializer, OPA_AUTH_TYPES + + +def _parse_exception_message(exception: PolicyEvaluationError): + pe_plain = str(exception.value) + + assert "This job cannot be executed due to a policy violation or error. See the following details:" in pe_plain + + violation_message = "This job cannot be executed due to a policy violation or error. See the following details:" + return eval(pe_plain.split(violation_message)[1].strip()) + + +@pytest.fixture(autouse=True) +def setup_opa_settings(): + with override_settings( + OPA_HOST='opa.example.com', + ): + yield + + +@pytest.fixture +def opa_client(): + cls_mock = mock.MagicMock(name='OpaClient') + instance_mock = cls_mock.return_value + instance_mock.__enter__.return_value = instance_mock + + with mock.patch('awx.main.tasks.policy.OpaClient', cls_mock): + yield instance_mock + + +@pytest.fixture +def job(): + project: Project = Project.objects.create(name='proj1', scm_type='git', scm_branch='main', scm_url='https://git.example.com/proj1') + inventory: Inventory = Inventory.objects.create(name='inv1', opa_query_path="inventory/response") + org: Organization = Organization.objects.create(name="org1", opa_query_path="organization/response") + jt: JobTemplate = JobTemplate.objects.create(name="jt1", opa_query_path="job_template/response") + job: Job = Job.objects.create(name='job1', extra_vars="{}", inventory=inventory, project=project, organization=org, job_template=jt) + return job + + +@pytest.mark.django_db +def test_job_serializer(): + user: User = User.objects.create(username='user1') + org: Organization = Organization.objects.create(name='org1') + + team: Team = Team.objects.create(name='team1', organization=org) + team.admin_role.members.add(user) + + project: Project = Project.objects.create(name='proj1', scm_type='git', scm_branch='main', scm_url='https://git.example.com/proj1') + inventory: Inventory = Inventory.objects.create(name='inv1', description='Demo inventory') + inventory_source: InventorySource = InventorySource.objects.create(name='inv-src1', source='file', inventory=inventory) + extra_vars = {"FOO": "value1", "BAR": "value2"} + + CredentialType.setup_tower_managed_defaults() + cred_type_ssh: CredentialType = CredentialType.objects.get(kind='ssh') + cred: Credential = Credential.objects.create(name="cred1", description='Demo credential', credential_type=cred_type_ssh, organization=org) + + label: Label = Label.objects.create(name='label1', organization=org) + + job: Job = Job.objects.create( + name='job1', extra_vars=json.dumps(extra_vars), inventory=inventory, project=project, organization=org, created_by=user, launch_type='workflow' + ) + # job.unified_job_node.workflow_job = workflow_job + job.credentials.add(cred) + job.labels.add(label) + + workflow_job: WorkflowJob = WorkflowJob.objects.create(name='wf-job1') + WorkflowJobNode.objects.create(job=job, workflow_job=workflow_job) + + serializer = JobSerializer(instance=job) + + assert serializer.data == { + 'id': job.id, + 'name': 'job1', + 'created': job.created.strftime("%Y-%m-%dT%H:%M:%S.%fZ"), + 'created_by': { + 'id': user.id, + 'username': 'user1', + 'is_superuser': False, + 'teams': [ + {'id': team.id, 'name': 'team1'}, + ], + }, + 'credentials': [ + { + 'id': cred.id, + 'name': 'cred1', + 'description': 'Demo credential', + 'organization': { + 'id': org.id, + 'name': 'org1', + }, + 'credential_type': cred_type_ssh.id, + 'kind': 'ssh', + 'managed': False, + 'kubernetes': False, + 'cloud': False, + }, + ], + 'execution_environment': None, + 'extra_vars': extra_vars, + 'forks': 0, + 'hosts_count': 0, + 'instance_group': None, + 'inventory': { + 'id': inventory.id, + 'name': 'inv1', + 'description': 'Demo inventory', + 'kind': '', + 'total_hosts': 0, + 'total_groups': 0, + 'has_inventory_sources': False, + 'total_inventory_sources': 0, + 'has_active_failures': False, + 'hosts_with_active_failures': 0, + 'inventory_sources': [ + { + 'id': inventory_source.id, + 'name': 'inv-src1', + 'source': 'file', + 'status': 'never updated', + } + ], + }, + 'job_template': None, + 'job_type': 'run', + 'job_type_name': 'job', + 'labels': [ + { + 'id': label.id, + 'name': 'label1', + 'organization': { + 'id': org.id, + 'name': 'org1', + }, + }, + ], + 'launch_type': 'workflow', + 'limit': '', + 'launched_by': {}, + 'organization': { + 'id': org.id, + 'name': 'org1', + }, + 'playbook': '', + 'project': { + 'id': project.id, + 'name': 'proj1', + 'status': 'pending', + 'scm_type': 'git', + 'scm_url': 'https://git.example.com/proj1', + 'scm_branch': 'main', + 'scm_refspec': '', + 'scm_clean': False, + 'scm_track_submodules': False, + 'scm_delete_on_update': False, + }, + 'scm_branch': '', + 'scm_revision': '', + 'workflow_job': { + 'id': workflow_job.id, + 'name': 'wf-job1', + }, + 'workflow_job_template': None, + } + + +@pytest.mark.django_db +def test_evaluate_policy_missing_opa_query_path_field(opa_client): + project: Project = Project.objects.create(name='proj1', scm_type='git', scm_branch='main', scm_url='https://git.example.com/proj1') + inventory: Inventory = Inventory.objects.create(name='inv1') + org: Organization = Organization.objects.create(name="org1") + jt: JobTemplate = JobTemplate.objects.create(name="jt1") + job: Job = Job.objects.create(name='job1', extra_vars="{}", inventory=inventory, project=project, organization=org, job_template=jt) + + response = { + "result": { + "allowed": True, + "violations": [], + } + } + opa_client.query_rule.return_value = response + try: + policy.evaluate_policy(job) + except PolicyEvaluationError as e: + pytest.fail(f"Must not raise PolicyEvaluationError: {e}") + + assert opa_client.query_rule.call_count == 0 + + +@pytest.mark.django_db +def test_evaluate_policy(opa_client, job): + response = { + "result": { + "allowed": True, + "violations": [], + } + } + opa_client.query_rule.return_value = response + try: + policy.evaluate_policy(job) + except PolicyEvaluationError as e: + pytest.fail(f"Must not raise PolicyEvaluationError: {e}") + + opa_client.query_rule.assert_has_calls( + [ + mock.call(input_data=mock.ANY, package_path='organization/response'), + mock.call(input_data=mock.ANY, package_path='inventory/response'), + mock.call(input_data=mock.ANY, package_path='job_template/response'), + ], + any_order=False, + ) + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +def test_evaluate_policy_allowed(opa_client, job): + response = { + "result": { + "allowed": True, + "violations": [], + } + } + opa_client.query_rule.return_value = response + try: + policy.evaluate_policy(job) + except PolicyEvaluationError as e: + pytest.fail(f"Must not raise PolicyEvaluationError: {e}") + + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +def test_evaluate_policy_not_allowed(opa_client, job): + response = { + "result": { + "allowed": False, + "violations": ["Access not allowed."], + } + } + opa_client.query_rule.return_value = response + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + pe_plain = str(pe.value) + assert "Errors:" not in pe_plain + + exception = _parse_exception_message(pe) + + assert exception["Violations"]["Organization"] == ["Access not allowed."] + assert exception["Violations"]["Inventory"] == ["Access not allowed."] + assert exception["Violations"]["Job template"] == ["Access not allowed."] + + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +def test_evaluate_policy_not_found(opa_client, job): + response = {} + opa_client.query_rule.return_value = response + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + missing_result_property = 'Call to OPA did not return a "result" property. The path refers to an undefined document.' + + exception = _parse_exception_message(pe) + assert exception["Errors"]["Organization"] == missing_result_property + assert exception["Errors"]["Inventory"] == missing_result_property + assert exception["Errors"]["Job template"] == missing_result_property + + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +def test_evaluate_policy_server_error(opa_client, job): + http_error_msg = '500 Server Error: Internal Server Error for url: https://opa.example.com:8181/v1/data/job_template/response/invalid' + error_response = { + 'code': 'internal_error', + 'message': ( + '1 error occurred: 1:1: rego_type_error: undefined ref: data.job_template.response.invalid\n\t' + 'data.job_template.response.invalid\n\t' + ' ^\n\t' + ' have: "invalid"\n\t' + ' want (one of): ["allowed" "violations"]' + ), + } + response = mock.Mock() + response.status_code = requests.codes.internal_server_error + response.json.return_value = error_response + + opa_client.query_rule.side_effect = requests.exceptions.HTTPError(http_error_msg, response=response) + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + exception = _parse_exception_message(pe) + assert exception["Errors"]["Organization"] == f'Call to OPA failed. Code: internal_error, Message: {error_response["message"]}' + assert exception["Errors"]["Inventory"] == f'Call to OPA failed. Code: internal_error, Message: {error_response["message"]}' + assert exception["Errors"]["Job template"] == f'Call to OPA failed. Code: internal_error, Message: {error_response["message"]}' + + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +def test_evaluate_policy_invalid_result(opa_client, job): + response = { + "result": { + "absolutely": "no!", + } + } + opa_client.query_rule.return_value = response + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + invalid_result = 'OPA policy returned invalid result.' + + exception = _parse_exception_message(pe) + assert exception["Errors"]["Organization"] == invalid_result + assert exception["Errors"]["Inventory"] == invalid_result + assert exception["Errors"]["Job template"] == invalid_result + + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +def test_evaluate_policy_failed_exception(opa_client, job): + error_response = {} + response = mock.Mock() + response.status_code = requests.codes.internal_server_error + response.json.return_value = error_response + + opa_client.query_rule.side_effect = ValueError("Invalid JSON") + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + opa_failed_exception = 'Call to OPA failed. Exception: Invalid JSON' + + exception = _parse_exception_message(pe) + assert exception["Errors"]["Organization"] == opa_failed_exception + assert exception["Errors"]["Inventory"] == opa_failed_exception + assert exception["Errors"]["Job template"] == opa_failed_exception + + assert opa_client.query_rule.call_count == 3 + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "settings_kwargs, expected_client_cert, expected_verify, verify_content", + [ + # Case 1: Certificate-based authentication (mTLS) + ( + { + "OPA_HOST": "opa.example.com", + "OPA_SSL": True, + "OPA_AUTH_TYPE": OPA_AUTH_TYPES.CERTIFICATE, + "OPA_AUTH_CLIENT_CERT": "-----BEGIN CERTIFICATE-----\nMIICert\n-----END CERTIFICATE-----", + "OPA_AUTH_CLIENT_KEY": "-----BEGIN PRIVATE KEY-----\nMIIKey\n-----END PRIVATE KEY-----", + "OPA_AUTH_CA_CERT": "-----BEGIN CERTIFICATE-----\nMIICACert\n-----END CERTIFICATE-----", + }, + True, # Client cert should be created + "file", # Verify path should be a file + "-----BEGIN CERTIFICATE-----", # Expected content in verify file + ), + # Case 2: SSL with server verification only + ( + { + "OPA_HOST": "opa.example.com", + "OPA_SSL": True, + "OPA_AUTH_TYPE": OPA_AUTH_TYPES.NONE, + "OPA_AUTH_CA_CERT": "-----BEGIN CERTIFICATE-----\nMIICACert\n-----END CERTIFICATE-----", + }, + False, # No client cert should be created + "file", # Verify path should be a file + "-----BEGIN CERTIFICATE-----", # Expected content in verify file + ), + # Case 3: SSL with system CA store + ( + { + "OPA_HOST": "opa.example.com", + "OPA_SSL": True, + "OPA_AUTH_TYPE": OPA_AUTH_TYPES.NONE, + "OPA_AUTH_CA_CERT": "", # No custom CA cert + }, + False, # No client cert should be created + True, # Verify path should be True (system CA store) + None, # No file to check content + ), + # Case 4: No SSL + ( + { + "OPA_HOST": "opa.example.com", + "OPA_SSL": False, + "OPA_AUTH_TYPE": OPA_AUTH_TYPES.NONE, + }, + False, # No client cert should be created + False, # Verify path should be False (no verification) + None, # No file to check content + ), + ], + ids=[ + "certificate_auth", + "ssl_server_verification", + "ssl_system_ca_store", + "no_ssl", + ], +) +def test_opa_cert_file(settings_kwargs, expected_client_cert, expected_verify, verify_content): + """Parameterized test for the opa_cert_file context manager. + + Tests different configurations: + - Certificate-based authentication (mTLS) + - SSL with server verification only + - SSL with system CA store + - No SSL + """ + with override_settings(**settings_kwargs): + client_cert_path = None + verify_path = None + + with policy.opa_cert_file() as cert_files: + client_cert_path, verify_path = cert_files + + # Check client cert based on expected_client_cert + if expected_client_cert: + assert client_cert_path is not None + with open(client_cert_path, 'r') as f: + content = f.read() + assert "-----BEGIN CERTIFICATE-----" in content + assert "-----BEGIN PRIVATE KEY-----" in content + else: + assert client_cert_path is None + + # Check verify path based on expected_verify + if expected_verify == "file": + assert verify_path is not None + assert os.path.isfile(verify_path) + with open(verify_path, 'r') as f: + content = f.read() + assert verify_content in content + else: + assert verify_path is expected_verify + + # Verify files are deleted after context manager exits + if expected_client_cert: + assert not os.path.exists(client_cert_path), "Client cert file was not deleted" + + if expected_verify == "file": + assert not os.path.exists(verify_path), "CA cert file was not deleted" + + +@pytest.mark.django_db +@override_settings( + OPA_HOST='opa.example.com', + OPA_SSL=False, # SSL disabled + OPA_AUTH_TYPE=OPA_AUTH_TYPES.CERTIFICATE, # But cert auth enabled + OPA_AUTH_CLIENT_CERT="-----BEGIN CERTIFICATE-----\nMIICert\n-----END CERTIFICATE-----", + OPA_AUTH_CLIENT_KEY="-----BEGIN PRIVATE KEY-----\nMIIKey\n-----END PRIVATE KEY-----", +) +def test_evaluate_policy_cert_auth_requires_ssl(): + """Test that policy evaluation raises an error when certificate auth is used without SSL.""" + project = Project.objects.create(name='proj1') + inventory = Inventory.objects.create(name='inv1', opa_query_path="inventory/response") + org = Organization.objects.create(name="org1", opa_query_path="organization/response") + jt = JobTemplate.objects.create(name="jt1", opa_query_path="job_template/response") + job = Job.objects.create(name='job1', extra_vars="{}", inventory=inventory, project=project, organization=org, job_template=jt) + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + assert "OPA_AUTH_TYPE=Certificate requires OPA_SSL to be enabled" in str(pe.value) + + +@pytest.mark.django_db +@override_settings( + OPA_HOST='opa.example.com', + OPA_SSL=True, + OPA_AUTH_TYPE=OPA_AUTH_TYPES.CERTIFICATE, + OPA_AUTH_CLIENT_CERT="", # Missing client cert + OPA_AUTH_CLIENT_KEY="", # Missing client key + OPA_AUTH_CA_CERT="", # Missing CA cert +) +def test_evaluate_policy_missing_cert_settings(): + """Test that policy evaluation raises an error when certificate settings are missing.""" + project = Project.objects.create(name='proj1') + inventory = Inventory.objects.create(name='inv1', opa_query_path="inventory/response") + org = Organization.objects.create(name="org1", opa_query_path="organization/response") + jt = JobTemplate.objects.create(name="jt1", opa_query_path="job_template/response") + job = Job.objects.create(name='job1', extra_vars="{}", inventory=inventory, project=project, organization=org, job_template=jt) + + with pytest.raises(PolicyEvaluationError) as pe: + policy.evaluate_policy(job) + + error_msg = str(pe.value) + assert "Following certificate settings are missing for OPA_AUTH_TYPE=Certificate:" in error_msg + assert "OPA_AUTH_CLIENT_CERT" in error_msg + assert "OPA_AUTH_CLIENT_KEY" in error_msg + assert "OPA_AUTH_CA_CERT" in error_msg + + +@pytest.mark.django_db +@override_settings( + OPA_HOST='opa.example.com', + OPA_PORT=8181, + OPA_SSL=True, + OPA_AUTH_TYPE=OPA_AUTH_TYPES.CERTIFICATE, + OPA_AUTH_CLIENT_CERT="-----BEGIN CERTIFICATE-----\nMIICert\n-----END CERTIFICATE-----", + OPA_AUTH_CLIENT_KEY="-----BEGIN PRIVATE KEY-----\nMIIKey\n-----END PRIVATE KEY-----", + OPA_AUTH_CA_CERT="-----BEGIN CERTIFICATE-----\nMIICACert\n-----END CERTIFICATE-----", + OPA_REQUEST_TIMEOUT=2.5, + OPA_REQUEST_RETRIES=3, +) +def test_opa_client_context_manager_mtls(): + """Test that opa_client context manager correctly initializes the OPA client.""" + # Mock the OpaClient class + with mock.patch('awx.main.tasks.policy.OpaClient') as mock_opa_client: + # Setup the mock + mock_instance = mock_opa_client.return_value + mock_instance.__enter__.return_value = mock_instance + mock_instance._session = mock.MagicMock() + + # Use the context manager + with policy.opa_client(headers={'Custom-Header': 'Value'}) as client: + # Verify the client was initialized with the correct parameters + mock_opa_client.assert_called_once_with( + host='opa.example.com', + port=8181, + headers={'Custom-Header': 'Value'}, + ssl=True, + cert=mock.ANY, # We can't check the exact value as it's a temporary file + timeout=2.5, + retries=3, + ) + + # Verify the session properties were set correctly + assert client._session.cert is not None + assert client._session.verify is not None + + # Check the content of the cert file + cert_file_path = client._session.cert + assert os.path.isfile(cert_file_path) + with open(cert_file_path, 'r') as f: + cert_content = f.read() + assert "-----BEGIN CERTIFICATE-----" in cert_content + assert "MIICert" in cert_content + assert "-----BEGIN PRIVATE KEY-----" in cert_content + assert "MIIKey" in cert_content + + # Check the content of the verify file + verify_file_path = client._session.verify + assert os.path.isfile(verify_file_path) + with open(verify_file_path, 'r') as f: + verify_content = f.read() + assert "-----BEGIN CERTIFICATE-----" in verify_content + assert "MIICACert" in verify_content + + # Verify the client is the mocked instance + assert client is mock_instance + + # Store file paths for checking after context exit + cert_path = client._session.cert + verify_path = client._session.verify + + # Verify files are deleted after context manager exits + assert not os.path.exists(cert_path), "Client cert file was not deleted" + assert not os.path.exists(verify_path), "CA cert file was not deleted" + + +@pytest.mark.django_db +@override_settings( + OPA_HOST='opa.example.com', + OPA_SSL=True, + OPA_AUTH_TYPE=OPA_AUTH_TYPES.TOKEN, + OPA_AUTH_TOKEN='secret-token', + OPA_AUTH_CUSTOM_HEADERS={'X-Custom': 'Header'}, +) +def test_opa_client_token_auth(): + """Test that token authentication correctly adds the Authorization header.""" + # Create a job for testing + project = Project.objects.create(name='proj1') + inventory = Inventory.objects.create(name='inv1', opa_query_path="inventory/response") + org = Organization.objects.create(name="org1", opa_query_path="organization/response") + jt = JobTemplate.objects.create(name="jt1", opa_query_path="job_template/response") + job = Job.objects.create(name='job1', extra_vars="{}", inventory=inventory, project=project, organization=org, job_template=jt) + + # Mock the OpaClient class + with mock.patch('awx.main.tasks.policy.opa_client') as mock_opa_client_cm: + # Setup the mock + mock_client = mock.MagicMock() + mock_opa_client_cm.return_value.__enter__.return_value = mock_client + mock_client.query_rule.return_value = { + "result": { + "allowed": True, + "violations": [], + } + } + + # Call evaluate_policy + policy.evaluate_policy(job) + + # Verify opa_client was called with the correct headers + expected_headers = {'X-Custom': 'Header', 'Authorization': 'Bearer secret-token'} + mock_opa_client_cm.assert_called_once_with(headers=expected_headers) diff --git a/awx/main/tests/functional/test_projects.py b/awx/main/tests/functional/test_projects.py index 0459aaab49ae..7d389d1e1623 100644 --- a/awx/main/tests/functional/test_projects.py +++ b/awx/main/tests/functional/test_projects.py @@ -4,7 +4,7 @@ import pytest from awx.api.versioning import reverse -from awx.main.models import Project +from awx.main.models import Project, JobTemplate from django.core.exceptions import ValidationError @@ -334,8 +334,71 @@ def test_team_project_list(get, team_project_list): ) +@pytest.mark.django_db +def test_project_teams_list_multiple_roles_distinct(get, organization_factory): + # test projects with multiple roles on the same team + objects = organization_factory( + 'org1', + superusers=['admin'], + teams=['teamA'], + projects=['proj1'], + roles=[ + 'teamA.member_role:proj1.admin_role', + 'teamA.member_role:proj1.use_role', + 'teamA.member_role:proj1.update_role', + 'teamA.member_role:proj1.read_role', + ], + ) + admin = objects.superusers.admin + proj1 = objects.projects.proj1 + + res = get(reverse('api:project_teams_list', kwargs={'pk': proj1.pk}), admin).data + names = [t['name'] for t in res['results']] + assert names == ['teamA'] + + +@pytest.mark.django_db +def test_project_teams_list_multiple_teams(get, organization_factory): + # test projects with multiple teams + objs = organization_factory( + 'org1', + superusers=['admin'], + teams=['teamA', 'teamB', 'teamC', 'teamD'], + projects=['proj1'], + roles=[ + 'teamA.member_role:proj1.admin_role', + 'teamB.member_role:proj1.update_role', + 'teamC.member_role:proj1.use_role', + 'teamD.member_role:proj1.read_role', + ], + ) + admin = objs.superusers.admin + proj1 = objs.projects.proj1 + + res = get(reverse('api:project_teams_list', kwargs={'pk': proj1.pk}), admin).data + names = sorted([t['name'] for t in res['results']]) + assert names == ['teamA', 'teamB', 'teamC', 'teamD'] + + +@pytest.mark.django_db +def test_project_teams_list_no_direct_assignments(get, organization_factory): + # test projects with no direct team assignments + objects = organization_factory( + 'org1', + superusers=['admin'], + teams=['teamA'], + projects=['proj1'], + roles=[], + ) + admin = objects.superusers.admin + proj1 = objects.projects.proj1 + + res = get(reverse('api:project_teams_list', kwargs={'pk': proj1.pk}), admin).data + assert res['count'] == 0 + + @pytest.mark.parametrize("u,expected_status_code", [('rando', 403), ('org_member', 403), ('org_admin', 201), ('admin', 201)]) -@pytest.mark.django_db() +@pytest.mark.django_db def test_create_project(post, organization, org_admin, org_member, admin, rando, u, expected_status_code): if u == 'rando': u = rando @@ -353,11 +416,12 @@ def test_create_project(post, organization, org_admin, org_member, admin, rando, 'organization': organization.id, }, u, + expect=expected_status_code, ) - print(result.data) - assert result.status_code == expected_status_code if expected_status_code == 201: assert Project.objects.filter(name='Project', organization=organization).exists() + elif expected_status_code == 403: + assert 'do not have permission' in str(result.data['detail']) @pytest.mark.django_db @@ -411,14 +475,14 @@ def test_project_delete(delete, organization, admin_user): @pytest.mark.parametrize( - 'order_by, expected_names, expected_ids', + 'order_by, expected_names', [ - ('name', ['alice project', 'bob project', 'shared project'], [1, 2, 3]), - ('-name', ['shared project', 'bob project', 'alice project'], [3, 2, 1]), + ('name', ['alice project', 'bob project', 'shared project']), + ('-name', ['shared project', 'bob project', 'alice project']), ], ) @pytest.mark.django_db -def test_project_list_ordering_by_name(get, order_by, expected_names, expected_ids, organization_factory): +def test_project_list_ordering_by_name(get, order_by, expected_names, organization_factory): 'ensure sorted order of project list is maintained correctly when the requested order is invalid or not applicable' objects = organization_factory( 'org1', @@ -426,28 +490,44 @@ def test_project_list_ordering_by_name(get, order_by, expected_names, expected_i superusers=['admin'], ) project_names = [] - project_ids = [] # TODO: ask for an order by here that doesn't apply results = get(reverse('api:project_list'), objects.superusers.admin, QUERY_STRING='order_by=%s' % order_by).data['results'] for x in range(len(results)): project_names.append(results[x]['name']) - project_ids.append(results[x]['id']) - assert project_names == expected_names and project_ids == expected_ids + assert project_names == expected_names @pytest.mark.parametrize('order_by', ('name', '-name')) @pytest.mark.django_db -def test_project_list_ordering_with_duplicate_names(get, order_by, organization_factory): +def test_project_list_ordering_with_duplicate_names(get, order_by, admin): # why? because all the '1' mean that all the names are the same, you can't sort based on that, # meaning you have to fall back on the default sort order, which in this case, is ID 'ensure sorted order of project list is maintained correctly when the project names the same' - objects = organization_factory( - 'org1', - projects=['1', '1', '1', '1', '1'], - superusers=['admin'], - ) + from awx.main.models import Organization + + projects = [] + for i in range(5): + projects.append(Project.objects.create(name='1', organization=Organization.objects.create(name=f'org{i}'))) project_ids = {} for x in range(3): - results = get(reverse('api:project_list'), objects.superusers.admin, QUERY_STRING='order_by=%s' % order_by).data['results'] + results = get(reverse('api:project_list'), user=admin, QUERY_STRING='order_by=%s' % order_by).data['results'] project_ids[x] = [proj['id'] for proj in results] - assert project_ids[0] == project_ids[1] == project_ids[2] == [1, 2, 3, 4, 5] + assert project_ids[0] == project_ids[1] == project_ids[2] + assert project_ids[0] == sorted(project_ids[0]) + assert set(project_ids[0]) == set([proj.id for proj in projects]) + + +@pytest.mark.django_db +def test_project_failed_update(post, project, admin, inventory): + """Test to ensure failed projects with update on launch will create launch rather than error""" + jt = JobTemplate.objects.create(project=project, inventory=inventory) + # set project to update on launch and set status to failed + project.update_fields(scm_update_on_launch=True) + project.update() + project.project_updates.last().update_fields(status='failed') + response = post(reverse('api:job_template_launch', kwargs={'pk': jt.pk}), user=admin, expect=201) + assert response.status_code == 201 + # set project to not update on launch and validate still 400's + project.update_fields(scm_update_on_launch=False) + response = post(reverse('api:job_template_launch', kwargs={'pk': jt.pk}), user=admin, expect=400) + assert response.status_code == 400 diff --git a/awx/main/tests/functional/test_python_requirements.py b/awx/main/tests/functional/test_python_requirements.py index d363b91db1f4..089638e5c2fa 100644 --- a/awx/main/tests/functional/test_python_requirements.py +++ b/awx/main/tests/functional/test_python_requirements.py @@ -5,7 +5,38 @@ from django.conf import settings -@pytest.mark.skip(reason="This test needs some love") +def test_bootstrap_consistent(): + with open('Makefile', 'r') as f: + mk_data = f.read() + bootstrap_reqs = None + for line in mk_data.split('\n'): + if line.startswith('VENV_BOOTSTRAP'): + parts = line.split() + bootstrap_reqs = parts[parts.index('?=') + 1 :] + break + else: + raise RuntimeError('Cound not find bootstrap line') + + req_data = None + with open('requirements/requirements.txt', 'r') as f: + req_data = f.read() + + different_requirements = [] + for req in bootstrap_reqs: + boot_req_name, _ = req.split('=', 1) + for line in req_data.split('\n'): + if '=' not in line: + continue + req_name, _ = line.split('=', 1) + if req_name == boot_req_name: + if req != line: + different_requirements.append((req, line)) + break + + assert not different_requirements + + +@pytest.mark.xfail(reason="This test needs some love") def test_env_matches_requirements_txt(): from pip.operations import freeze @@ -33,7 +64,7 @@ def skip_line(line): if skip_line(x): continue x = x.lower() - (pkg_name, pkg_version) = x.split('==') + pkg_name, pkg_version = x.split('==') reqs_actual.append([pkg_name, pkg_version]) reqs_expected = [] @@ -49,7 +80,7 @@ def skip_line(line): Special case pkg_name[pkg_subname]==version For this case, we strip out [pkg_subname] ''' - (pkg_name, pkg_version) = line.split('==') + pkg_name, pkg_version = line.split('==') pkg_name = re.sub(r'\[.*\]', '', pkg_name) reqs_expected.append([pkg_name, pkg_version]) diff --git a/awx/main/tests/functional/test_rbac_core.py b/awx/main/tests/functional/test_rbac_core.py deleted file mode 100644 index 7029bbe54486..000000000000 --- a/awx/main/tests/functional/test_rbac_core.py +++ /dev/null @@ -1,213 +0,0 @@ -import pytest - -from awx.main.models import ( - Role, - Organization, - Project, -) -from awx.main.fields import update_role_parentage_for_instance - - -@pytest.mark.django_db -def test_auto_inheritance_by_children(organization, alice): - A = Role.objects.create() - B = Role.objects.create() - A.members.add(alice) - - assert alice not in organization.admin_role - assert Organization.accessible_objects(alice, 'admin_role').count() == 0 - A.children.add(B) - assert alice not in organization.admin_role - assert Organization.accessible_objects(alice, 'admin_role').count() == 0 - A.children.add(organization.admin_role) - assert alice in organization.admin_role - assert Organization.accessible_objects(alice, 'admin_role').count() == 1 - A.children.remove(organization.admin_role) - assert alice not in organization.admin_role - B.children.add(organization.admin_role) - assert alice in organization.admin_role - B.children.remove(organization.admin_role) - assert alice not in organization.admin_role - assert Organization.accessible_objects(alice, 'admin_role').count() == 0 - - # We've had the case where our pre/post save init handlers in our field descriptors - # end up creating a ton of role objects because of various not-so-obvious issues - assert Role.objects.count() < 50 - - -@pytest.mark.django_db -def test_auto_inheritance_by_parents(organization, alice): - A = Role.objects.create() - B = Role.objects.create() - A.members.add(alice) - - assert alice not in organization.admin_role - B.parents.add(A) - assert alice not in organization.admin_role - organization.admin_role.parents.add(A) - assert alice in organization.admin_role - organization.admin_role.parents.remove(A) - assert alice not in organization.admin_role - organization.admin_role.parents.add(B) - assert alice in organization.admin_role - organization.admin_role.parents.remove(B) - assert alice not in organization.admin_role - - -@pytest.mark.django_db -def test_accessible_objects(organization, alice, bob): - A = Role.objects.create() - A.members.add(alice) - B = Role.objects.create() - B.members.add(alice) - B.members.add(bob) - - assert Organization.accessible_objects(alice, 'admin_role').count() == 0 - assert Organization.accessible_objects(bob, 'admin_role').count() == 0 - A.children.add(organization.admin_role) - assert Organization.accessible_objects(alice, 'admin_role').count() == 1 - assert Organization.accessible_objects(bob, 'admin_role').count() == 0 - - -@pytest.mark.django_db -def test_team_symantics(organization, team, alice): - assert alice not in organization.auditor_role - team.member_role.children.add(organization.auditor_role) - assert alice not in organization.auditor_role - team.member_role.members.add(alice) - assert alice in organization.auditor_role - team.member_role.members.remove(alice) - assert alice not in organization.auditor_role - - -@pytest.mark.django_db -def test_auto_field_adjustments(organization, inventory, team, alice): - 'Ensures the auto role reparenting is working correctly through non m2m fields' - org2 = Organization.objects.create(name='Org 2', description='org 2') - org2.admin_role.members.add(alice) - assert alice not in inventory.admin_role - inventory.organization = org2 - inventory.save() - assert alice in inventory.admin_role - inventory.organization = organization - inventory.save() - assert alice not in inventory.admin_role - # assert False - - -@pytest.mark.django_db -def test_implicit_deletes(alice): - 'Ensures implicit resources and roles delete themselves' - delorg = Organization.objects.create(name='test-org') - child = Role.objects.create() - child.parents.add(delorg.admin_role) - delorg.admin_role.members.add(alice) - - admin_role_id = delorg.admin_role.id - auditor_role_id = delorg.auditor_role.id - - assert child.ancestors.count() > 1 - assert Role.objects.filter(id=admin_role_id).count() == 1 - assert Role.objects.filter(id=auditor_role_id).count() == 1 - n_alice_roles = alice.roles.count() - n_system_admin_children = Role.singleton('system_administrator').children.count() - - delorg.delete() - - assert Role.objects.filter(id=admin_role_id).count() == 0 - assert Role.objects.filter(id=auditor_role_id).count() == 0 - assert alice.roles.count() == (n_alice_roles - 1) - assert Role.singleton('system_administrator').children.count() == (n_system_admin_children - 1) - assert child.ancestors.count() == 1 - assert child.ancestors.all()[0] == child - - -@pytest.mark.django_db -def test_content_object(user): - 'Ensure our content_object stuf seems to be working' - - org = Organization.objects.create(name='test-org') - assert org.admin_role.content_object.id == org.id - - -@pytest.mark.django_db -def test_hierarchy_rebuilding_multi_path(): - 'Tests a subdtle cases around role hierarchy rebuilding when you have multiple paths to the same role of different length' - - X = Role.objects.create() - A = Role.objects.create() - B = Role.objects.create() - C = Role.objects.create() - D = Role.objects.create() - - A.children.add(B) - A.children.add(D) - B.children.add(C) - C.children.add(D) - - assert A.is_ancestor_of(D) - assert X.is_ancestor_of(D) is False - - X.children.add(A) - - assert X.is_ancestor_of(D) is True - - X.children.remove(A) - - # This can be the stickler, the rebuilder needs to ensure that D's role - # hierarchy is built after both A and C are updated. - assert X.is_ancestor_of(D) is False - - -@pytest.mark.django_db -def test_auto_parenting(): - org1 = Organization.objects.create(name='org1') - org2 = Organization.objects.create(name='org2') - - prj1 = Project.objects.create(name='prj1') - prj2 = Project.objects.create(name='prj2') - - assert org1.admin_role.is_ancestor_of(prj1.admin_role) is False - assert org1.admin_role.is_ancestor_of(prj2.admin_role) is False - assert org2.admin_role.is_ancestor_of(prj1.admin_role) is False - assert org2.admin_role.is_ancestor_of(prj2.admin_role) is False - - prj1.organization = org1 - prj1.save() - - assert org1.admin_role.is_ancestor_of(prj1.admin_role) - assert org1.admin_role.is_ancestor_of(prj2.admin_role) is False - assert org2.admin_role.is_ancestor_of(prj1.admin_role) is False - assert org2.admin_role.is_ancestor_of(prj2.admin_role) is False - - prj2.organization = org1 - prj2.save() - - assert org1.admin_role.is_ancestor_of(prj1.admin_role) - assert org1.admin_role.is_ancestor_of(prj2.admin_role) - assert org2.admin_role.is_ancestor_of(prj1.admin_role) is False - assert org2.admin_role.is_ancestor_of(prj2.admin_role) is False - - prj1.organization = org2 - prj1.save() - - assert org1.admin_role.is_ancestor_of(prj1.admin_role) is False - assert org1.admin_role.is_ancestor_of(prj2.admin_role) - assert org2.admin_role.is_ancestor_of(prj1.admin_role) - assert org2.admin_role.is_ancestor_of(prj2.admin_role) is False - - prj2.organization = org2 - prj2.save() - - assert org1.admin_role.is_ancestor_of(prj1.admin_role) is False - assert org1.admin_role.is_ancestor_of(prj2.admin_role) is False - assert org2.admin_role.is_ancestor_of(prj1.admin_role) - assert org2.admin_role.is_ancestor_of(prj2.admin_role) - - -@pytest.mark.django_db -def test_update_parents_keeps_teams(team, project): - project.update_role.parents.add(team.member_role) - assert team.member_role in project.update_role # test prep sanity check - update_role_parentage_for_instance(project) - assert team.member_role in project.update_role # actual assertion diff --git a/awx/main/tests/functional/test_rbac_instance_groups.py b/awx/main/tests/functional/test_rbac_instance_groups.py deleted file mode 100644 index 402040ea21c3..000000000000 --- a/awx/main/tests/functional/test_rbac_instance_groups.py +++ /dev/null @@ -1,87 +0,0 @@ -import pytest - -from awx.main.access import ( - InstanceGroupAccess, - OrganizationAccess, - InventoryAccess, - JobTemplateAccess, -) -from awx.main.models import Organization - - -@pytest.mark.django_db -def test_ig_normal_user_visibility(organization, default_instance_group, user): - u = user('user', False) - assert len(InstanceGroupAccess(u).get_queryset()) == 0 - organization.instance_groups.add(default_instance_group) - organization.member_role.members.add(u) - assert len(InstanceGroupAccess(u).get_queryset()) == 0 - - -@pytest.mark.django_db -def test_ig_admin_user_visibility(organization, default_instance_group, admin, system_auditor, org_admin): - assert len(InstanceGroupAccess(admin).get_queryset()) == 1 - assert len(InstanceGroupAccess(system_auditor).get_queryset()) == 1 - assert len(InstanceGroupAccess(org_admin).get_queryset()) == 0 - organization.instance_groups.add(default_instance_group) - assert len(InstanceGroupAccess(org_admin).get_queryset()) == 1 - - -@pytest.mark.django_db -def test_ig_normal_user_associability(organization, default_instance_group, user): - u = user('user', False) - access = OrganizationAccess(u) - assert not access.can_attach(organization, default_instance_group, 'instance_groups', None) - organization.instance_groups.add(default_instance_group) - organization.member_role.members.add(u) - assert not access.can_attach(organization, default_instance_group, 'instance_groups', None) - - -@pytest.mark.django_db -def test_access_via_two_organizations(rando, default_instance_group): - for org_name in ['org1', 'org2']: - org = Organization.objects.create(name=org_name) - org.instance_groups.add(default_instance_group) - org.admin_role.members.add(rando) - access = InstanceGroupAccess(rando) - assert list(access.get_queryset()) == [default_instance_group] - - -@pytest.mark.django_db -def test_ig_associability(organization, default_instance_group, admin, system_auditor, org_admin, org_member, job_template_factory): - admin_access = OrganizationAccess(admin) - auditor_access = OrganizationAccess(system_auditor) - oadmin_access = OrganizationAccess(org_admin) - omember_access = OrganizationAccess(org_member) - assert admin_access.can_attach(organization, default_instance_group, 'instance_groups', None) - assert not oadmin_access.can_attach(organization, default_instance_group, 'instance_groups', None) - assert not auditor_access.can_attach(organization, default_instance_group, 'instance_groups', None) - assert not omember_access.can_attach(organization, default_instance_group, 'instance_groups', None) - - organization.instance_groups.add(default_instance_group) - - assert admin_access.can_unattach(organization, default_instance_group, 'instance_groups', None) - assert not oadmin_access.can_unattach(organization, default_instance_group, 'instance_groups', None) - assert not auditor_access.can_unattach(organization, default_instance_group, 'instance_groups', None) - assert not omember_access.can_unattach(organization, default_instance_group, 'instance_groups', None) - - objects = job_template_factory('jt', organization=organization, project='p', inventory='i', credential='c') - admin_access = InventoryAccess(admin) - auditor_access = InventoryAccess(system_auditor) - oadmin_access = InventoryAccess(org_admin) - omember_access = InventoryAccess(org_member) - - assert admin_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) - assert oadmin_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) - assert not auditor_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) - assert not omember_access.can_attach(objects.inventory, default_instance_group, 'instance_groups', None) - - admin_access = JobTemplateAccess(admin) - auditor_access = JobTemplateAccess(system_auditor) - oadmin_access = JobTemplateAccess(org_admin) - omember_access = JobTemplateAccess(org_member) - - assert admin_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) - assert oadmin_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) - assert not auditor_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) - assert not omember_access.can_attach(objects.job_template, default_instance_group, 'instance_groups', None) diff --git a/awx/main/tests/functional/test_rbac_migration.py b/awx/main/tests/functional/test_rbac_migration.py deleted file mode 100644 index 5f1b2633e86e..000000000000 --- a/awx/main/tests/functional/test_rbac_migration.py +++ /dev/null @@ -1,75 +0,0 @@ -import pytest - -from django.apps import apps - -from awx.main.migrations import _rbac as rbac -from awx.main.models import UnifiedJobTemplate, InventorySource, Inventory, JobTemplate, Project, Organization, User - - -@pytest.mark.django_db -def test_implied_organization_subquery_inventory(): - orgs = [] - for i in range(3): - orgs.append(Organization.objects.create(name='foo{}'.format(i))) - orgs.append(orgs[0]) - for i in range(4): - org = orgs[i] - if i == 2: - inventory = Inventory.objects.create(name='foo{}'.format(i)) - else: - inventory = Inventory.objects.create(name='foo{}'.format(i), organization=org) - inv_src = InventorySource.objects.create(name='foo{}'.format(i), inventory=inventory, source='ec2') - sources = UnifiedJobTemplate.objects.annotate(test_field=rbac.implicit_org_subquery(UnifiedJobTemplate, InventorySource)) - for inv_src in sources: - assert inv_src.test_field == inv_src.inventory.organization_id - - -@pytest.mark.django_db -def test_implied_organization_subquery_job_template(): - jts = [] - for i in range(5): - if i <= 3: - org = Organization.objects.create(name='foo{}'.format(i)) - else: - org = None - if i <= 4: - proj = Project.objects.create(name='foo{}'.format(i), organization=org) - else: - proj = None - jts.append(JobTemplate.objects.create(name='foo{}'.format(i), project=proj)) - # test case of sharing same org - jts[2].project.organization = jts[3].project.organization - jts[2].save() - ujts = UnifiedJobTemplate.objects.annotate(test_field=rbac.implicit_org_subquery(UnifiedJobTemplate, JobTemplate)) - for jt in ujts: - if not isinstance(jt, JobTemplate): # some are projects - assert jt.test_field is None - else: - if jt.project is None: - assert jt.test_field is None - else: - assert jt.test_field == jt.project.organization_id - - -@pytest.mark.django_db -def test_give_explicit_inventory_permission(): - dual_admin = User.objects.create(username='alice') - inv_admin = User.objects.create(username='bob') - inv_org = Organization.objects.create(name='inv-org') - proj_org = Organization.objects.create(name='proj-org') - - inv_org.admin_role.members.add(inv_admin, dual_admin) - proj_org.admin_role.members.add(dual_admin) - - proj = Project.objects.create(name="test-proj", organization=proj_org) - inv = Inventory.objects.create(name='test-inv', organization=inv_org) - - jt = JobTemplate.objects.create(name='foo', project=proj, inventory=inv) - - assert dual_admin in jt.admin_role - - rbac.restore_inventory_admins(apps, None) - - assert inv_admin in jt.admin_role.members.all() - assert dual_admin not in jt.admin_role.members.all() - assert dual_admin in jt.admin_role diff --git a/awx/main/tests/functional/test_rbac_oauth.py b/awx/main/tests/functional/test_rbac_oauth.py deleted file mode 100644 index c55943adeb35..000000000000 --- a/awx/main/tests/functional/test_rbac_oauth.py +++ /dev/null @@ -1,247 +0,0 @@ -import pytest - -from awx.main.access import ( - OAuth2ApplicationAccess, - OAuth2TokenAccess, - ActivityStreamAccess, -) -from awx.main.models.oauth import ( - OAuth2Application as Application, - OAuth2AccessToken as AccessToken, -) -from awx.main.models import ActivityStream -from awx.api.versioning import reverse - - -@pytest.mark.django_db -class TestOAuth2Application: - @pytest.mark.parametrize( - "user_for_access, can_access_list", - [ - (0, [True, True]), - (1, [True, True]), - (2, [True, True]), - (3, [False, False]), - ], - ) - def test_can_read(self, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization): - user_list = [admin, org_admin, org_member, alice] - access = OAuth2ApplicationAccess(user_list[user_for_access]) - app_creation_user_list = [admin, org_admin] - for user, can_access in zip(app_creation_user_list, can_access_list): - app = Application.objects.create( - name='test app for {}'.format(user.username), - user=user, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - assert access.can_read(app) is can_access - - def test_admin_only_can_read(self, user, organization): - user = user('org-admin', False) - organization.admin_role.members.add(user) - access = OAuth2ApplicationAccess(user) - app = Application.objects.create( - name='test app for {}'.format(user.username), user=user, client_type='confidential', authorization_grant_type='password', organization=organization - ) - assert access.can_read(app) is True - - def test_app_activity_stream(self, org_admin, alice, organization): - app = Application.objects.create( - name='test app for {}'.format(org_admin.username), - user=org_admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - access = OAuth2ApplicationAccess(org_admin) - assert access.can_read(app) is True - access = ActivityStreamAccess(org_admin) - activity_stream = ActivityStream.objects.filter(o_auth2_application=app).latest('pk') - assert access.can_read(activity_stream) is True - access = ActivityStreamAccess(alice) - assert access.can_read(app) is False - assert access.can_read(activity_stream) is False - - def test_token_activity_stream(self, org_admin, alice, organization, post): - app = Application.objects.create( - name='test app for {}'.format(org_admin.username), - user=org_admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), {'scope': 'read'}, org_admin, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - access = OAuth2ApplicationAccess(org_admin) - assert access.can_read(app) is True - access = ActivityStreamAccess(org_admin) - activity_stream = ActivityStream.objects.filter(o_auth2_access_token=token).latest('pk') - assert access.can_read(activity_stream) is True - access = ActivityStreamAccess(alice) - assert access.can_read(token) is False - assert access.can_read(activity_stream) is False - - def test_can_edit_delete_app_org_admin(self, admin, org_admin, org_member, alice, organization): - user_list = [admin, org_admin, org_member, alice] - can_access_list = [True, True, False, False] - for user, can_access in zip(user_list, can_access_list): - app = Application.objects.create( - name='test app for {}'.format(user.username), - user=org_admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - access = OAuth2ApplicationAccess(user) - assert access.can_change(app, {}) is can_access - assert access.can_delete(app) is can_access - - def test_can_edit_delete_app_admin(self, admin, org_admin, org_member, alice, organization): - user_list = [admin, org_admin, org_member, alice] - can_access_list = [True, True, False, False] - for user, can_access in zip(user_list, can_access_list): - app = Application.objects.create( - name='test app for {}'.format(user.username), - user=admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - access = OAuth2ApplicationAccess(user) - assert access.can_change(app, {}) is can_access - assert access.can_delete(app) is can_access - - def test_superuser_can_always_create(self, admin, org_admin, org_member, alice, organization): - access = OAuth2ApplicationAccess(admin) - for user in [admin, org_admin, org_member, alice]: - assert access.can_add( - {'name': 'test app', 'user': user.pk, 'client_type': 'confidential', 'authorization_grant_type': 'password', 'organization': organization.id} - ) - - def test_normal_user_cannot_create(self, admin, org_admin, org_member, alice, organization): - for access_user in [org_member, alice]: - access = OAuth2ApplicationAccess(access_user) - for user in [admin, org_admin, org_member, alice]: - assert not access.can_add( - { - 'name': 'test app', - 'user': user.pk, - 'client_type': 'confidential', - 'authorization_grant_type': 'password', - 'organization': organization.id, - } - ) - - -@pytest.mark.django_db -class TestOAuth2Token: - def test_can_read_change_delete_app_token(self, post, admin, org_admin, org_member, alice, organization): - user_list = [admin, org_admin, org_member, alice] - can_access_list = [True, True, False, False] - app = Application.objects.create( - name='test app for {}'.format(admin.username), - user=admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), {'scope': 'read'}, admin, expect=201) - for user, can_access in zip(user_list, can_access_list): - token = AccessToken.objects.get(token=response.data['token']) - access = OAuth2TokenAccess(user) - assert access.can_read(token) is can_access - assert access.can_change(token, {}) is can_access - assert access.can_delete(token) is can_access - - def test_auditor_can_read(self, post, admin, org_admin, org_member, alice, system_auditor, organization): - user_list = [admin, org_admin, org_member] - can_access_list = [True, True, True] - cannot_access_list = [False, False, False] - app = Application.objects.create( - name='test app for {}'.format(admin.username), - user=admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - for user, can_access, cannot_access in zip(user_list, can_access_list, cannot_access_list): - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), {'scope': 'read'}, user, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - access = OAuth2TokenAccess(system_auditor) - assert access.can_read(token) is can_access - assert access.can_change(token, {}) is cannot_access - assert access.can_delete(token) is cannot_access - - def test_user_auditor_can_change(self, post, org_member, org_admin, system_auditor, organization): - app = Application.objects.create( - name='test app for {}'.format(org_admin.username), - user=org_admin, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - response = post(reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), {'scope': 'read'}, org_member, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - access = OAuth2TokenAccess(system_auditor) - assert access.can_read(token) is True - assert access.can_change(token, {}) is False - assert access.can_delete(token) is False - dual_user = system_auditor - organization.admin_role.members.add(dual_user) - access = OAuth2TokenAccess(dual_user) - assert access.can_read(token) is True - assert access.can_change(token, {}) is True - assert access.can_delete(token) is True - - def test_can_read_change_delete_personal_token_org_member(self, post, admin, org_admin, org_member, alice): - # Tests who can read a token created by an org-member - user_list = [admin, org_admin, org_member, alice] - can_access_list = [True, False, True, False] - response = post(reverse('api:user_personal_token_list', kwargs={'pk': org_member.pk}), {'scope': 'read'}, org_member, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - for user, can_access in zip(user_list, can_access_list): - access = OAuth2TokenAccess(user) - assert access.can_read(token) is can_access - assert access.can_change(token, {}) is can_access - assert access.can_delete(token) is can_access - - def test_can_read_personal_token_creator(self, post, admin, org_admin, org_member, alice): - # Tests the token's creator can read their tokens - user_list = [admin, org_admin, org_member, alice] - can_access_list = [True, True, True, True] - - for user, can_access in zip(user_list, can_access_list): - response = post(reverse('api:user_personal_token_list', kwargs={'pk': user.pk}), {'scope': 'read', 'application': None}, user, expect=201) - token = AccessToken.objects.get(token=response.data['token']) - access = OAuth2TokenAccess(user) - assert access.can_read(token) is can_access - assert access.can_change(token, {}) is can_access - assert access.can_delete(token) is can_access - - @pytest.mark.parametrize( - "user_for_access, can_access_list", - [ - (0, [True, True]), - (1, [True, True]), - (2, [True, True]), - (3, [False, False]), - ], - ) - def test_can_create(self, post, admin, org_admin, org_member, alice, user_for_access, can_access_list, organization): - user_list = [admin, org_admin, org_member, alice] - for user, can_access in zip(user_list, can_access_list): - app = Application.objects.create( - name='test app for {}'.format(user.username), - user=user, - client_type='confidential', - authorization_grant_type='password', - organization=organization, - ) - post( - reverse('api:o_auth2_application_token_list', kwargs={'pk': app.pk}), - {'scope': 'read'}, - user_list[user_for_access], - expect=201 if can_access else 403, - ) diff --git a/awx/main/tests/functional/test_routing.py b/awx/main/tests/functional/test_routing.py new file mode 100644 index 000000000000..7cb5e9b5874d --- /dev/null +++ b/awx/main/tests/functional/test_routing.py @@ -0,0 +1,90 @@ +import pytest + +from django.contrib.auth.models import AnonymousUser + +from channels.routing import ProtocolTypeRouter +from channels.testing.websocket import WebsocketCommunicator + + +from awx.main.consumers import WebsocketSecretAuthHelper + + +@pytest.fixture +def application(): + # code in routing hits the db on import because .. settings cache + from awx.main.routing import application_func + + yield application_func(ProtocolTypeRouter) + + +@pytest.fixture +def websocket_server_generator(application): + def fn(endpoint): + return WebsocketCommunicator(application, endpoint) + + return fn + + +@pytest.mark.asyncio +@pytest.mark.django_db +class TestWebsocketRelay: + @pytest.fixture + def websocket_relay_secret_generator(self, settings): + def fn(secret, set_broadcast_websocket_secret=False): + secret_backup = settings.BROADCAST_WEBSOCKET_SECRET + settings.BROADCAST_WEBSOCKET_SECRET = 'foobar' + res = ('secret'.encode('utf-8'), WebsocketSecretAuthHelper.construct_secret().encode('utf-8')) + if set_broadcast_websocket_secret is False: + settings.BROADCAST_WEBSOCKET_SECRET = secret_backup + return res + + return fn + + @pytest.fixture + def websocket_relay_secret(self, settings, websocket_relay_secret_generator): + return websocket_relay_secret_generator('foobar', set_broadcast_websocket_secret=True) + + async def test_authorized(self, websocket_server_generator, websocket_relay_secret): + server = websocket_server_generator('/websocket/relay/') + + server.scope['headers'] = (websocket_relay_secret,) + connected, _ = await server.connect() + assert connected is True + + async def test_not_authorized(self, websocket_server_generator): + server = websocket_server_generator('/websocket/relay/') + connected, _ = await server.connect() + assert connected is False, "Connection to the relay websocket without auth. We expected the client to be denied." + + async def test_wrong_secret(self, websocket_server_generator, websocket_relay_secret_generator): + server = websocket_server_generator('/websocket/relay/') + + server.scope['headers'] = (websocket_relay_secret_generator('foobar', set_broadcast_websocket_secret=False),) + connected, _ = await server.connect() + assert connected is False + + +@pytest.mark.asyncio +@pytest.mark.django_db +class TestWebsocketEventConsumer: + async def test_unauthorized_anonymous(self, websocket_server_generator): + server = websocket_server_generator('/websocket/') + + server.scope['user'] = AnonymousUser() + connected, _ = await server.connect() + assert connected is False, "Anonymous user should NOT be allowed to login." + + @pytest.mark.xfail(reason="Ran out of coding time.") + async def test_authorized(self, websocket_server_generator, application, admin): + server = websocket_server_generator('/websocket/') + + """ + I ran out of time. Here is what I was thinking ... + Inject a valid session into the cookies in the header + + server.scope['headers'] = ( + (b'cookie', ...), + ) + """ + connected, _ = await server.connect() + assert connected is True, "User should be allowed in via cookies auth via a session key in the cookies" diff --git a/awx/main/tests/functional/test_tasks.py b/awx/main/tests/functional/test_tasks.py deleted file mode 100644 index c4d0dac4e362..000000000000 --- a/awx/main/tests/functional/test_tasks.py +++ /dev/null @@ -1,89 +0,0 @@ -import pytest -from unittest import mock -import os -import tempfile -import shutil - -from awx.main.tasks.jobs import RunJob -from awx.main.tasks.system import execution_node_health_check, _cleanup_images_and_files, handle_work_error -from awx.main.models import Instance, Job, InventoryUpdate, ProjectUpdate - - -@pytest.fixture -def scm_revision_file(tmpdir_factory): - # Returns path to temporary testing revision file - revision_file = tmpdir_factory.mktemp('revisions').join('revision.txt') - with open(str(revision_file), 'w') as f: - f.write('1234567890123456789012345678901234567890') - return os.path.join(revision_file.dirname, 'revision.txt') - - -@pytest.mark.django_db -@pytest.mark.parametrize('node_type', ('control. hybrid')) -def test_no_worker_info_on_AWX_nodes(node_type): - hostname = 'us-south-3-compute.invalid' - Instance.objects.create(hostname=hostname, node_type=node_type) - assert execution_node_health_check(hostname) is None - - -@pytest.fixture -def mock_job_folder(request): - pdd_path = tempfile.mkdtemp(prefix='awx_123_') - - def test_folder_cleanup(): - if os.path.exists(pdd_path): - shutil.rmtree(pdd_path) - - request.addfinalizer(test_folder_cleanup) - - return pdd_path - - -@pytest.mark.django_db -def test_folder_cleanup_stale_file(mock_job_folder, mock_me): - _cleanup_images_and_files() - assert os.path.exists(mock_job_folder) # grace period should protect folder from deletion - - _cleanup_images_and_files(grace_period=0) - assert not os.path.exists(mock_job_folder) # should be deleted - - -@pytest.mark.django_db -def test_folder_cleanup_running_job(mock_job_folder, mock_me): - me_inst = Instance.objects.create(hostname='local_node', uuid='00000000-0000-0000-0000-000000000000') - with mock.patch.object(Instance.objects, 'me', return_value=me_inst): - job = Job.objects.create(id=123, controller_node=me_inst.hostname, status='running') - _cleanup_images_and_files(grace_period=0) - assert os.path.exists(mock_job_folder) # running job should prevent folder from getting deleted - - job.status = 'failed' - job.save(update_fields=['status']) - _cleanup_images_and_files(grace_period=0) - assert not os.path.exists(mock_job_folder) # job is finished and no grace period, should delete - - -@pytest.mark.django_db -def test_does_not_run_reaped_job(mocker, mock_me): - job = Job.objects.create(status='failed', job_explanation='This job has been reaped.') - mock_run = mocker.patch('awx.main.tasks.jobs.ansible_runner.interface.run') - try: - RunJob().run(job.id) - except Exception: - pass - job.refresh_from_db() - assert job.status == 'failed' - mock_run.assert_not_called() - - -@pytest.mark.django_db -def test_handle_work_error_nested(project, inventory_source): - pu = ProjectUpdate.objects.create(status='failed', project=project, celery_task_id='1234') - iu = InventoryUpdate.objects.create(status='pending', inventory_source=inventory_source, source='scm') - job = Job.objects.create(status='pending') - iu.dependent_jobs.add(pu) - job.dependent_jobs.add(pu, iu) - handle_work_error({'type': 'project_update', 'id': pu.id}) - iu.refresh_from_db() - job.refresh_from_db() - assert iu.job_explanation == f'Previous Task Failed: {{"job_type": "project_update", "job_name": "", "job_id": "{pu.id}"}}' - assert job.job_explanation == f'Previous Task Failed: {{"job_type": "inventory_update", "job_name": "", "job_id": "{iu.id}"}}' diff --git a/awx/main/tests/functional/test_teams.py b/awx/main/tests/functional/test_teams.py deleted file mode 100644 index eda57579cebd..000000000000 --- a/awx/main/tests/functional/test_teams.py +++ /dev/null @@ -1,14 +0,0 @@ -import pytest - - -@pytest.mark.django_db() -def test_admin_not_member(team): - """Test to ensure we don't add admin_role as a parent to team.member_role, as - this creates a cycle with organization administration, which we've decided - to remove support for - - (2016-06-16) I think this might have been resolved. I'm asserting - this to be true in the mean time. - """ - - assert team.admin_role.is_ancestor_of(team.member_role) is True diff --git a/awx/main/tests/live/pytest.ini b/awx/main/tests/live/pytest.ini new file mode 100644 index 000000000000..cc1e9c5a91de --- /dev/null +++ b/awx/main/tests/live/pytest.ini @@ -0,0 +1,3 @@ +# This file is needed to undo the pytest settings from the project root +[pytest] +addopts = -p no:django -p awx.main.tests.live.pytest_django_config diff --git a/awx/main/tests/live/pytest_django_config.py b/awx/main/tests/live/pytest_django_config.py new file mode 100644 index 000000000000..8c4eac2474b5 --- /dev/null +++ b/awx/main/tests/live/pytest_django_config.py @@ -0,0 +1,12 @@ +import django + +from awx import prepare_env + + +def pytest_load_initial_conftests(args): + """Replacement for same-named method in pytest_django plugin + + Instead of setting up a test database, this just sets up Django normally + this will give access to the postgres database as-is, for better and worse""" + prepare_env() + django.setup() diff --git a/awx/main/tests/live/tests/api/test_uniqueness.py b/awx/main/tests/live/tests/api/test_uniqueness.py new file mode 100644 index 000000000000..e32b9416bfef --- /dev/null +++ b/awx/main/tests/live/tests/api/test_uniqueness.py @@ -0,0 +1,77 @@ +import multiprocessing +import json + +import pytest + +import requests +from requests.auth import HTTPBasicAuth + +from django.db import connection + +from awx.main.models import User, JobTemplate + + +def create_in_subprocess(project_id, ready_event, continue_event, admin_auth): + connection.connect() + + print('setting ready event') + ready_event.set() + print('waiting for continue event') + continue_event.wait() + + if JobTemplate.objects.filter(name='test_jt_duplicate_name').exists(): + for jt in JobTemplate.objects.filter(name='test_jt_duplicate_name'): + jt.delete() + assert JobTemplate.objects.filter(name='test_jt_duplicate_name').count() == 0 + + jt_data = {'name': 'test_jt_duplicate_name', 'project': project_id, 'playbook': 'hello_world.yml', 'ask_inventory_on_launch': True} + response = requests.post('http://localhost:8013/api/v2/job_templates/', json=jt_data, auth=admin_auth) + # should either have a conflict or create + assert response.status_code in (400, 201) + print(f'Subprocess got {response.status_code}') + if response.status_code == 400: + print(json.dumps(response.json(), indent=2)) + return response.status_code + + +@pytest.fixture +def admin_for_test(): + user, created = User.objects.get_or_create(username='admin_for_test', defaults={'is_superuser': True}) + if created: + user.set_password('for_test_123!') + user.save() + print(f'Created user {user.username}') + return user + + +@pytest.fixture +def admin_auth(admin_for_test): + return HTTPBasicAuth(admin_for_test.username, 'for_test_123!') + + +def test_jt_duplicate_name(admin_auth, demo_proj): + N_processes = 5 + ready_events = [multiprocessing.Event() for _ in range(N_processes)] + continue_event = multiprocessing.Event() + + processes = [] + for i in range(N_processes): + p = multiprocessing.Process(target=create_in_subprocess, args=(demo_proj.id, ready_events[i], continue_event, admin_auth)) + processes.append(p) + p.start() + + # Assure both processes are connected and have loaded their host list + for e in ready_events: + print('waiting on subprocess ready event') + e.wait() + + # Begin the bulk_update queries + print('setting the continue event for the workers') + continue_event.set() + + # if a Deadloack happens it will probably be surfaced by result here + print('waiting on the workers to finish the creation') + for p in processes: + p.join() + + assert JobTemplate.objects.filter(name='test_jt_duplicate_name').count() == 1 diff --git a/awx/main/tests/live/tests/conftest.py b/awx/main/tests/live/tests/conftest.py new file mode 100644 index 000000000000..92cfe213afcf --- /dev/null +++ b/awx/main/tests/live/tests/conftest.py @@ -0,0 +1,246 @@ +import subprocess +import time +import os +import shutil +import tempfile +import logging + +import pytest + +from django.conf import settings +from django.core.cache import cache + +from awx.api.versioning import reverse + +# These tests are invoked from the awx/main/tests/live/ subfolder +# so any fixtures from higher-up conftest files must be explicitly included +from awx.main.tests.functional.conftest import * # noqa +from awx.main.tests.conftest import load_all_credentials # noqa: F401; pylint: disable=unused-import +from awx.main.tests import data + +from awx.main.models import Project, JobTemplate, Organization, Inventory, WorkflowJob, UnifiedJob +from awx.main.tasks.system import clear_setting_cache + +logger = logging.getLogger(__name__) + + +PROJ_DATA = os.path.join(os.path.dirname(data.__file__), 'projects') +COLL_DATA = os.path.join(os.path.dirname(data.__file__), 'collections') + + +def _copy_folders(source_path, dest_path, clear=False): + "folder-by-folder, copy dirs in the source root dir to the destination root dir" + for dirname in os.listdir(source_path): + source_dir = os.path.join(source_path, dirname) + expected_dir = os.path.join(dest_path, dirname) + if clear and os.path.exists(expected_dir): + shutil.rmtree(expected_dir) + if (not os.path.isdir(source_dir)) or os.path.exists(expected_dir): + continue + shutil.copytree(source_dir, expected_dir) + + +GIT_COMMANDS = ( + 'git config --global init.defaultBranch devel; ' + 'git init; ' + 'git config user.email jenkins@ansible.com; ' + 'git config user.name DoneByTest; ' + 'git add .; ' + 'git commit -m "initial commit"' +) + + +@pytest.fixture(scope='session') +def live_tmp_folder(): + path = os.path.join(tempfile.gettempdir(), 'live_tests') + if os.path.exists(path): + shutil.rmtree(path) + os.mkdir(path) + _copy_folders(PROJ_DATA, path) + _copy_folders(COLL_DATA, path) + for dirname in os.listdir(path): + source_dir = os.path.join(path, dirname) + subprocess.run(GIT_COMMANDS, cwd=source_dir, shell=True) + # force invalidation of key before checking it in case it is stale + cache.delete_many(['AWX_ISOLATION_SHOW_PATHS']) + settings._awx_conf_memoizedcache.clear() + if path not in settings.AWX_ISOLATION_SHOW_PATHS: + logger.info(f'Modifying settings.AWX_ISOLATION_SHOW_PATHS for live test: {settings.AWX_ISOLATION_SHOW_PATHS + [path]}') + settings.AWX_ISOLATION_SHOW_PATHS = settings.AWX_ISOLATION_SHOW_PATHS + [path] + cache.delete_many(['AWX_ISOLATION_SHOW_PATHS']) + settings._awx_conf_memoizedcache.clear() + # cache is cleared in test environment, but need to clear in test environment + clear_setting_cache.delay(['AWX_ISOLATION_SHOW_PATHS']) + time.sleep(5.0) # for _awx_conf_memoizedcache to expire on all workers + else: + logger.info(f'Believed that {path} is already in settings.AWX_ISOLATION_SHOW_PATHS: {settings.AWX_ISOLATION_SHOW_PATHS}') + return path + + +def wait_to_leave_status(job, status, timeout=30, sleep_time=0.1): + """Wait until the job does NOT have the specified status with some timeout + + the default timeout is based on the task manager running a 20 second + schedule, and the API does not guarentee working jobs faster than this + """ + start = time.time() + while time.time() - start < timeout: + job.refresh_from_db() + if job.status != status: + return + time.sleep(sleep_time) + raise RuntimeError(f'Job failed to exit {status} in {timeout} seconds. job_explanation={job.job_explanation} tb={job.result_traceback}') + + +def wait_for_events(uj, timeout=2): + start = time.time() + while uj.event_processing_finished is False: + time.sleep(0.2) + uj.refresh_from_db() + if time.time() - start > timeout: + break + + +def unified_job_stdout(uj): + if type(uj) is UnifiedJob: + uj = uj.get_real_instance() + if isinstance(uj, WorkflowJob): + outputs = [] + for node in uj.workflow_job_nodes.all().select_related('job').order_by('id'): + if node.job is None: + continue + outputs.append( + 'workflow node {node_id} job {job_id} output:\n{output}'.format( + node_id=node.id, + job_id=node.job.id, + output=unified_job_stdout(node.job), + ) + ) + return '\n'.join(outputs) + wait_for_events(uj) + return '\n'.join([event.stdout for event in uj.get_event_queryset().order_by('created')]) + + +def wait_for_job(job, final_status='successful', running_timeout=800): + wait_to_leave_status(job, 'pending') + wait_to_leave_status(job, 'waiting') + wait_to_leave_status(job, 'running', timeout=running_timeout) + + assert job.status == final_status, f'Job was not successful id={job.id} status={job.status} tb={job.result_traceback} output=\n{unified_job_stdout(job)}' + + +@pytest.fixture(scope='session') +def default_org(): + org = Organization.objects.filter(name='Default').first() + if org is None: + raise Exception('Tests expect Default org to already be created and it is not') + return org + + +@pytest.fixture(scope='session') +def demo_inv(default_org): + inventory, _ = Inventory.objects.get_or_create(name='Demo Inventory', defaults={'organization': default_org}) + return inventory + + +@pytest.fixture(scope='session') +def demo_proj(default_org): + proj, _ = Project.objects.get_or_create(name='Demo Project', defaults={'organization': default_org}) + return proj + + +@pytest.fixture +def podman_image_generator(): + """ + Generate a tagless podman image from awx base EE + """ + + def fn(): + dockerfile = """ + FROM quay.io/ansible/awx-ee:latest + RUN echo "Hello, Podman!" > /tmp/hello.txt + """ + cmd = ['podman', 'build', '-f', '-'] # Create an image without a tag + subprocess.run(cmd, capture_output=True, input=dockerfile, text=True, check=True) + + return fn + + +@pytest.fixture +def project_factory(post, default_org, admin): + def _rf(scm_url=None, local_path=None, **extra_kwargs): + proj_kwargs = {} + if local_path: + # manual path + project_name = f'Manual roject {local_path}' + proj_kwargs['scm_type'] = '' + proj_kwargs['local_path'] = local_path + elif scm_url: + project_name = f'Project {scm_url}' + proj_kwargs['scm_type'] = 'git' + proj_kwargs['scm_url'] = scm_url + else: + raise RuntimeError('Need to provide scm_url or local_path') + + if extra_kwargs: + proj_kwargs.update(extra_kwargs) + + proj_kwargs['name'] = project_name + proj_kwargs['organization'] = default_org.id + + old_proj = Project.objects.filter(name=project_name).first() + if old_proj: + logger.info(f'Deleting existing project {project_name}') + old_proj.delete() + + result = post( + reverse('api:project_list'), + proj_kwargs, + admin, + expect=201, + ) + proj = Project.objects.get(id=result.data['id']) + return proj + + return _rf + + +@pytest.fixture +def run_job_from_playbook(demo_inv, post, admin, project_factory): + def _rf(test_name, playbook, local_path=None, scm_url=None, jt_params=None, proj=None, wait=True): + jt_name = f'{test_name} JT: {playbook}' + + if not proj: + proj = project_factory(scm_url=scm_url, local_path=local_path) + + old_jt = JobTemplate.objects.filter(name=jt_name).first() + if old_jt: + logger.info(f'Deleting existing JT {jt_name}') + old_jt.delete() + + if proj.current_job: + wait_for_job(proj.current_job) + + assert proj.get_project_path() + assert playbook in proj.playbooks + + jt_data = {'name': jt_name, 'project': proj.id, 'playbook': playbook, 'inventory': demo_inv.id} + if jt_params: + jt_data.update(jt_params) + + result = post( + reverse('api:job_template_list'), + jt_data, + admin, + expect=201, + ) + jt = JobTemplate.objects.get(id=result.data['id']) + job = jt.create_unified_job() + job.signal_start() + + if wait: + wait_for_job(job) + assert job.status == 'successful' + return {'job': job, 'job_template': jt, 'project': proj} + + return _rf diff --git a/awx/main/tests/live/tests/dispatcherd/test_connection_recovery.py b/awx/main/tests/live/tests/dispatcherd/test_connection_recovery.py new file mode 100644 index 000000000000..183d9ca1d5ac --- /dev/null +++ b/awx/main/tests/live/tests/dispatcherd/test_connection_recovery.py @@ -0,0 +1,74 @@ +import time + +from dispatcherd.config import settings +from dispatcherd.factories import get_control_from_settings +from dispatcherd.utils import serialize_task + +from awx.main.models import JobTemplate + +from awx.main.tests.data.sleep_task import sleep_break_connection, advisory_lock_exception +from awx.main.tests.live.tests.conftest import wait_for_job + + +def poll_for_task_finish(task_name): + running_tasks = [1] + start = time.monotonic() + ctl = get_control_from_settings() + while running_tasks: + responses = ctl.control_with_reply('running') + assert len(responses) == 1 + response = responses[0] + response.pop('node_id') + running_tasks = [task_data for task_data in response.values() if task_data['task'] == task_name] + if time.monotonic() - start > 5.0: + assert False, f'Never finished working through tasks: {running_tasks}' + + +def check_jobs_work(): + jt = JobTemplate.objects.get(name='Demo Job Template') + job = jt.create_unified_job() + job.signal_start() + wait_for_job(job) + + +def test_advisory_lock_error_clears(): + """Run a task that has an exception while holding advisory_lock + + This is regression testing for a bug in its exception handling + expected to be fixed by + https://github.com/ansible/django-ansible-base/pull/713 + + This is an "easier" test case than the next, + because it passes just by fixing the DAB case, + and passing this does not generally guarentee that + workers will not be left with a connection in a bad state. + """ + min_workers = settings.service['pool_kwargs']['min_workers'] + + for i in range(min_workers): + advisory_lock_exception.delay() + + task_name = serialize_task(advisory_lock_exception) + poll_for_task_finish(task_name) + + # Jobs should still work even after the breaking task has ran + check_jobs_work() + + +def test_can_recover_connection(): + """Run a task that intentionally times out the worker connection + + If no connection fixing is implemented outside of that task scope, + then subsequent tasks will all error, thus checking that jobs run, + after running the sleep_break_connection task. + """ + min_workers = settings.service['pool_kwargs']['min_workers'] + + for i in range(min_workers): + sleep_break_connection.delay() + + task_name = serialize_task(sleep_break_connection) + poll_for_task_finish(task_name) + + # Jobs should still work even after the breaking task has ran + check_jobs_work() diff --git a/awx/main/tests/live/tests/projects/conftest.py b/awx/main/tests/live/tests/projects/conftest.py new file mode 100644 index 000000000000..39c8b76fbfad --- /dev/null +++ b/awx/main/tests/live/tests/projects/conftest.py @@ -0,0 +1,14 @@ +import pytest +import os + +from django.conf import settings + +from awx.main.tests.live.tests.conftest import _copy_folders, PROJ_DATA + + +@pytest.fixture(scope='session') +def copy_project_folders(): + proj_root = settings.PROJECTS_ROOT + if not os.path.exists(proj_root): + os.mkdir(proj_root) + _copy_folders(PROJ_DATA, proj_root, clear=True) diff --git a/awx/main/tests/live/tests/projects/test_file_projects.py b/awx/main/tests/live/tests/projects/test_file_projects.py new file mode 100644 index 000000000000..616578b13699 --- /dev/null +++ b/awx/main/tests/live/tests/projects/test_file_projects.py @@ -0,0 +1,25 @@ +import os +import subprocess + +import pytest + +from awx.main.tests.live.tests.conftest import wait_for_job + + +def test_git_file_project(live_tmp_folder, run_job_from_playbook): + run_job_from_playbook('test_git_file_project', 'debug.yml', scm_url=f'file://{live_tmp_folder}/debug') + + +@pytest.mark.parametrize('allow_override', [True, False]) +def test_amend_commit(live_tmp_folder, project_factory, allow_override): + proj = project_factory(scm_url=f'file://{live_tmp_folder}/debug', allow_override=allow_override) + assert proj.current_job + wait_for_job(proj.current_job) + assert proj.allow_override is allow_override + + source_dir = os.path.join(live_tmp_folder, 'debug') + subprocess.run('git commit --amend --no-edit', cwd=source_dir, shell=True) + + update = proj.update() + update.signal_start() + wait_for_job(update) diff --git a/awx/main/tests/live/tests/projects/test_manual_project.py b/awx/main/tests/live/tests/projects/test_manual_project.py new file mode 100644 index 000000000000..11aeb76cf8b3 --- /dev/null +++ b/awx/main/tests/live/tests/projects/test_manual_project.py @@ -0,0 +1,2 @@ +def test_manual_project(copy_project_folders, run_job_from_playbook): + run_job_from_playbook('test_manual_project', 'debug.yml', local_path='debug') diff --git a/awx/main/tests/live/tests/projects/test_requirements.py b/awx/main/tests/live/tests/projects/test_requirements.py new file mode 100644 index 000000000000..c0d392996907 --- /dev/null +++ b/awx/main/tests/live/tests/projects/test_requirements.py @@ -0,0 +1,64 @@ +import os +import time + +import pytest + +from django.conf import settings + +from awx.main.tests.live.tests.conftest import wait_for_job, wait_for_events + +from awx.main.models import Project, SystemJobTemplate, Job + + +@pytest.fixture(scope='session') +def project_with_requirements(default_org): + project, _ = Project.objects.get_or_create( + name='project-with-requirements', + scm_url='https://github.com/ansible/test-playbooks.git', + scm_branch="with_requirements", + scm_type='git', + organization=default_org, + ) + start = time.time() + while time.time() - start < 3.0: + if project.current_job or project.last_job or project.last_job_run: + break + assert project.current_job or project.last_job or project.last_job_run, f'Project never updated id={project.id}' + update = project.current_job or project.last_job + if update: + wait_for_job(update) + return project + + +def project_cache_is_populated(project): + proj_cache = os.path.join(project.get_cache_path(), project.cache_id) + return os.path.exists(proj_cache) + + +def test_cache_is_populated_after_cleanup_job(project_with_requirements): + assert project_with_requirements.cache_id is not None # already updated, should be something + cache_path = os.path.join(settings.PROJECTS_ROOT, '.__awx_cache') + assert os.path.exists(cache_path) + + assert project_cache_is_populated(project_with_requirements) + + cleanup_sjt = SystemJobTemplate.objects.get(name='Cleanup Job Details') + cleanup_job = cleanup_sjt.create_unified_job(extra_vars={'days': 0}) + cleanup_job.signal_start() + wait_for_job(cleanup_job) + + project_with_requirements.refresh_from_db() + assert project_with_requirements.cache_id is not None + update = project_with_requirements.update() + wait_for_job(update) + + # Now, we still have a populated cache + assert project_cache_is_populated(project_with_requirements) + + +def test_git_file_collection_requirement(live_tmp_folder, copy_project_folders, run_job_from_playbook): + # this behaves differently, as use_requirements.yml references only the folder, does not include the github name + run_job_from_playbook('test_git_file_collection_requirement', 'use_requirement.yml', scm_url=f'file://{live_tmp_folder}/with_requirements') + job = Job.objects.filter(name__icontains='test_git_file_collection_requirement').order_by('-created').first() + wait_for_events(job) + assert '1234567890' in job.job_events.filter(task='debug variable', event='runner_on_ok').first().stdout diff --git a/awx/main/tests/live/tests/test_ansible_facts.py b/awx/main/tests/live/tests/test_ansible_facts.py new file mode 100644 index 000000000000..f6db48345e79 --- /dev/null +++ b/awx/main/tests/live/tests/test_ansible_facts.py @@ -0,0 +1,64 @@ +import pytest + +from awx.main.tests.live.tests.conftest import wait_for_events + +from awx.main.models import Job, Inventory + + +def assert_facts_populated(name): + job = Job.objects.filter(name__icontains=name).order_by('-created').first() + assert job is not None + wait_for_events(job) + + inventory = job.inventory + assert inventory.hosts.count() > 0 # sanity + for host in inventory.hosts.all(): + assert host.ansible_facts + + +@pytest.fixture +def general_facts_test(live_tmp_folder, run_job_from_playbook): + def _rf(slug, jt_params): + jt_params['use_fact_cache'] = True + standard_kwargs = dict(scm_url=f'file://{live_tmp_folder}/facts', jt_params=jt_params) + + # GATHER FACTS + name = f'test_gather_ansible_facts_{slug}' + run_job_from_playbook(name, 'gather.yml', **standard_kwargs) + assert_facts_populated(name) + + # KEEP FACTS + name = f'test_clear_ansible_facts_{slug}' + run_job_from_playbook(name, 'no_op.yml', **standard_kwargs) + assert_facts_populated(name) + + # CLEAR FACTS + name = f'test_clear_ansible_facts_{slug}' + run_job_from_playbook(name, 'clear.yml', **standard_kwargs) + job = Job.objects.filter(name__icontains=name).order_by('-created').first() + + assert job is not None + wait_for_events(job) + inventory = job.inventory + assert inventory.hosts.count() > 0 # sanity + for host in inventory.hosts.all(): + assert not host.ansible_facts + + return _rf + + +def test_basic_ansible_facts(general_facts_test): + general_facts_test('basic', {}) + + +@pytest.fixture +def sliced_inventory(): + inv, _ = Inventory.objects.get_or_create(name='inventory-to-slice') + if not inv.hosts.exists(): + for i in range(10): + inv.hosts.create(name=f'sliced_host_{i}') + return inv + + +def test_slicing_with_facts(general_facts_test, sliced_inventory): + general_facts_test('sliced', {'job_slice_count': 3, 'inventory': sliced_inventory.id}) diff --git a/awx/main/tests/live/tests/test_cleanup_task.py b/awx/main/tests/live/tests/test_cleanup_task.py new file mode 100644 index 000000000000..e9af90b96196 --- /dev/null +++ b/awx/main/tests/live/tests/test_cleanup_task.py @@ -0,0 +1,82 @@ +import os +import json +import pytest +import tempfile +import subprocess + +from unittest import mock + +from awx.main.tasks.receptor import _convert_args_to_cli, run_until_complete +from awx.main.tasks.system import CleanupImagesAndFiles +from awx.main.models import Instance, JobTemplate + + +def get_podman_images(): + cmd = ['podman', 'images', '--format', 'json'] + return json.loads((subprocess.run(cmd, capture_output=True, text=True, check=True)).stdout) + + +def test_folder_cleanup_multiple_running_jobs_execution_node(request): + demo_jt = JobTemplate.objects.get(name='Demo Job Template') + + jobs = [demo_jt.create_unified_job(_eager_fields={'status': 'running'}) for i in range(3)] + + def delete_jobs(): + for job in jobs: + job.delete() + + request.addfinalizer(delete_jobs) + + job_dirs = [] + job_patterns = [] + for job in jobs: + job_pattern = f'awx_{job.id}_1234' + job_dir = os.path.join(tempfile.gettempdir(), job_pattern) + job_patterns.append(job_pattern) + job_dirs.append(job_dir) + os.mkdir(job_dir) + + inst = Instance.objects.me() + runner_cleanup_kwargs = inst.get_cleanup_task_kwargs(exclude_strings=job_patterns, grace_period=0) + + # We can not call worker_cleanup directly because execution and control nodes are not fungible + args = _convert_args_to_cli(runner_cleanup_kwargs) + remote_command = ' '.join(args) + + subprocess.call('ansible-runner worker ' + remote_command, shell=True) + print('ansible-runner worker ' + remote_command) + + assert [os.path.exists(job_dir) for job_dir in job_dirs] == [True for i in range(3)] + + +@pytest.mark.parametrize( + 'worktype', + ('remote', 'local'), +) +def test_tagless_image(podman_image_generator, worktype: str): + """ + Ensure podman images on Control and Hybrid nodes are deleted during cleanup. + """ + podman_image_generator() + + dangling_image = next((image for image in get_podman_images() if image.get('Dangling', False)), None) + assert dangling_image + + instance_me = Instance.objects.me() + + match worktype: + case 'local': + CleanupImagesAndFiles.run_local(instance_me, image_prune=True) + case 'remote': + with ( + mock.patch( + 'awx.main.tasks.receptor.run_until_complete', lambda *args, **kwargs: run_until_complete(*args, worktype='local', ttl=None, **kwargs) + ), + mock.patch('awx.main.tasks.system.CleanupImagesAndFiles.get_execution_instances', lambda: [Instance.objects.me()]), + ): + CleanupImagesAndFiles.run_remote(instance_me, image_prune=True) + case _: + raise ValueError(f'worktype "{worktype}" not supported.') + + for image in get_podman_images(): + assert image['Id'] != dangling_image['Id'] diff --git a/awx/main/tests/live/tests/test_concurrent_fact_cache.py b/awx/main/tests/live/tests/test_concurrent_fact_cache.py new file mode 100644 index 000000000000..1bbd2337f006 --- /dev/null +++ b/awx/main/tests/live/tests/test_concurrent_fact_cache.py @@ -0,0 +1,351 @@ +"""Tests for concurrent fact caching with --limit. + +Reproduces bugs where concurrent jobs targeting different hosts via --limit +incorrectly modify (clear or revert) facts for hosts outside their limit. + +Customer report: concurrent jobs on the same job template with different limits +cause facts set by an earlier-finishing job to be rolled back when the +later-finishing job completes. + +See: https://github.com/jritter/concurrent-aap-fact-caching + +Generated by Claude Opus 4.6 (claude-opus-4-6). +""" + +import logging + +import pytest + +from django.utils.timezone import now + +from awx.api.versioning import reverse +from awx.main.models import Inventory, JobTemplate +from awx.main.tests.live.tests.conftest import wait_for_job, wait_to_leave_status + +logger = logging.getLogger(__name__) + + +@pytest.fixture +def concurrent_facts_inventory(default_org): + """Inventory with two hosts for concurrent fact cache testing.""" + inv_name = 'test_concurrent_fact_cache' + Inventory.objects.filter(organization=default_org, name=inv_name).delete() + inv = Inventory.objects.create(organization=default_org, name=inv_name) + inv.hosts.create(name='cc_host_0') + inv.hosts.create(name='cc_host_1') + return inv + + +@pytest.fixture +def concurrent_facts_jt(concurrent_facts_inventory, live_tmp_folder, post, admin, project_factory): + """Job template configured for concurrent fact-cached runs.""" + proj = project_factory(scm_url=f'file://{live_tmp_folder}/facts') + if proj.current_job: + wait_for_job(proj.current_job) + assert 'gather_slow.yml' in proj.playbooks, f'gather_slow.yml not in {proj.playbooks}' + + jt_name = 'test_concurrent_fact_cache JT' + existing_jt = JobTemplate.objects.filter(name=jt_name).first() + if existing_jt: + existing_jt.delete() + result = post( + reverse('api:job_template_list'), + { + 'name': jt_name, + 'project': proj.id, + 'playbook': 'gather_slow.yml', + 'inventory': concurrent_facts_inventory.id, + 'use_fact_cache': True, + 'allow_simultaneous': True, + }, + admin, + expect=201, + ) + return JobTemplate.objects.get(id=result.data['id']) + + +def test_concurrent_limit_does_not_clear_facts(concurrent_facts_inventory, concurrent_facts_jt): + """Concurrent jobs with different --limit must not clear each other's facts. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + + Scenario: + - Inventory has cc_host_0 and cc_host_1, neither has prior facts + - Job A runs gather_slow.yml with limit=cc_host_0 + - While Job A is still running (sleeping), Job B launches with limit=cc_host_1 + - Both jobs set cacheable facts, but only for their respective limited host + - After both complete, BOTH hosts should have populated facts + + The bug: get_hosts_for_fact_cache() returns ALL inventory hosts regardless + of --limit. start_fact_cache records them all in hosts_cached but writes + no fact files (no prior facts). When the later-finishing job runs + finish_fact_cache, it sees a missing fact file for the other job's host + and clears that host's facts. + """ + inv = concurrent_facts_inventory + jt = concurrent_facts_jt + + # Launch Job A targeting cc_host_0 + job_a = jt.create_unified_job() + job_a.limit = 'cc_host_0' + job_a.save(update_fields=['limit']) + job_a.signal_start() + + # Wait for Job A to reach running (it will sleep inside the playbook) + wait_to_leave_status(job_a, 'pending') + wait_to_leave_status(job_a, 'waiting') + logger.info(f'Job A (id={job_a.id}) is now running with limit=cc_host_0') + + # Launch Job B targeting cc_host_1 while Job A is still running + job_b = jt.create_unified_job() + job_b.limit = 'cc_host_1' + job_b.save(update_fields=['limit']) + job_b.signal_start() + + # Verify that Job A is still running when Job B starts, + # otherwise the overlap that triggers the bug did not happen. + wait_to_leave_status(job_b, 'pending') + wait_to_leave_status(job_b, 'waiting') + job_a.refresh_from_db() + if job_a.status != 'running': + pytest.skip('Job A finished before Job B started running; overlap did not occur') + logger.info(f'Job B (id={job_b.id}) is now running with limit=cc_host_1 (concurrent with Job A)') + + # Wait for both to complete + wait_for_job(job_a) + wait_for_job(job_b) + + # Verify facts survived concurrent execution + host_0 = inv.hosts.get(name='cc_host_0') + host_1 = inv.hosts.get(name='cc_host_1') + + # sanity + job_a.refresh_from_db() + job_b.refresh_from_db() + assert job_a.limit == "cc_host_0" + assert job_b.limit == "cc_host_1" + + discovered_foos = [host_0.ansible_facts.get('foo'), host_1.ansible_facts.get('foo')] + assert discovered_foos == ['bar'] * 2, f'Unexpected facts on cc_host_0 or _1: {discovered_foos} after job a,b {job_a.id}, {job_b.id}' + + +def test_concurrent_limit_does_not_revert_facts(live_tmp_folder, run_job_from_playbook, concurrent_facts_inventory): + """Concurrent jobs must not revert facts that a prior concurrent job just set. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + + Scenario: + - First, populate both hosts with initial facts (foo=bar) via a + non-concurrent gather run + - Then run two concurrent jobs with different limits, each setting + a new value (foo=bar_v2 via extra_vars) + - After both complete, BOTH hosts should have foo=bar_v2 + + The bug: start_fact_cache writes the OLD facts (foo=bar) into each job's + artifact dir for ALL hosts. If ansible's cache plugin rewrites a non-limited + host's fact file with the stale content (updating the mtime), finish_fact_cache + treats it as a legitimate update and overwrites the DB with old values. + """ + # --- Seed both hosts with initial facts via a non-concurrent run --- + inv = concurrent_facts_inventory + + scm_url = f'file://{live_tmp_folder}/facts' + res = run_job_from_playbook( + 'seed_facts_for_revert_test', + 'gather_slow.yml', + scm_url=scm_url, + jt_params={'use_fact_cache': True, 'allow_simultaneous': True, 'inventory': inv.id}, + ) + for host in inv.hosts.all(): + assert host.ansible_facts.get('foo') == 'bar', f'Seed run failed to set facts on {host.name}: {host.ansible_facts}' + + job = res['job'] + wait_for_job(job) + + # sanity, jobs should be set up to both have facts with just bar + host_0 = inv.hosts.get(name='cc_host_0') + host_1 = inv.hosts.get(name='cc_host_1') + discovered_foos = [host_0.ansible_facts.get('foo'), host_1.ansible_facts.get('foo')] + assert discovered_foos == ['bar'] * 2, f'Facts did not get expected initial values: {discovered_foos}' + + jt = job.job_template + assert jt.allow_simultaneous is True + assert jt.use_fact_cache is True + + # Sanity assertion, sometimes this would give problems from the Django rel cache + assert jt.project + + # --- Run two concurrent jobs that write a new value --- + # Update the JT to pass extra_vars that change the fact value + jt.extra_vars = '{"extra_value": "_v2"}' + jt.save(update_fields=['extra_vars']) + + job_a = jt.create_unified_job() + job_a.limit = 'cc_host_0' + job_a.save(update_fields=['limit']) + job_a.signal_start() + + wait_to_leave_status(job_a, 'pending') + wait_to_leave_status(job_a, 'waiting') + + job_b = jt.create_unified_job() + job_b.limit = 'cc_host_1' + job_b.save(update_fields=['limit']) + job_b.signal_start() + + wait_to_leave_status(job_b, 'pending') + wait_to_leave_status(job_b, 'waiting') + job_a.refresh_from_db() + if job_a.status != 'running': + pytest.skip('Job A finished before Job B started running; overlap did not occur') + + wait_for_job(job_a) + wait_for_job(job_b) + + host_0 = inv.hosts.get(name='cc_host_0') + host_1 = inv.hosts.get(name='cc_host_1') + + # Both hosts should have the UPDATED value, not the old seed value + discovered_foos = [host_0.ansible_facts.get('foo'), host_1.ansible_facts.get('foo')] + assert discovered_foos == ['bar_v2'] * 2, f'Facts were reverted to stale values by concurrent job cc_host_0 or cc_host_1: {discovered_foos}' + + +def test_fact_cache_scoped_to_inventory(live_tmp_folder, default_org, run_job_from_playbook): + """finish_fact_cache must not modify hosts in other inventories. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + + Bug: finish_fact_cache queries Host.objects.filter(name__in=host_names) + without an inventory_id filter, so hosts with the same name in different + inventories get their facts cross-contaminated. + """ + shared_name = 'scope_shared_host' + + # Prepare for test by deleting junk from last run + for inv_name in ('test_fact_scope_inv1', 'test_fact_scope_inv2'): + inv = Inventory.objects.filter(name=inv_name).first() + if inv: + inv.delete() + + inv1 = Inventory.objects.create(organization=default_org, name='test_fact_scope_inv1') + inv1.hosts.create(name=shared_name) + + inv2 = Inventory.objects.create(organization=default_org, name='test_fact_scope_inv2') + host2 = inv2.hosts.create(name=shared_name) + + # Give inv2's host distinct facts that should not be touched + original_facts = {'source': 'inventory_2', 'untouched': True} + host2.ansible_facts = original_facts + host2.ansible_facts_modified = now() + host2.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + # Run a fact-gathering job against inv1 only + run_job_from_playbook( + 'test_fact_scope', + 'gather.yml', + scm_url=f'file://{live_tmp_folder}/facts', + jt_params={'use_fact_cache': True, 'inventory': inv1.id}, + ) + + # inv1's host should have facts + host1 = inv1.hosts.get(name=shared_name) + assert host1.ansible_facts, f'inv1 host should have facts after gather: {host1.ansible_facts}' + + # inv2's host must NOT have been touched + host2.refresh_from_db() + assert host2.ansible_facts == original_facts, ( + f'Host in a different inventory was modified by a fact cache operation ' + f'on another inventory sharing the same hostname. ' + f'Expected {original_facts!r}, got {host2.ansible_facts!r}' + ) + + +def test_constructed_inventory_facts_saved_to_source_host(live_tmp_folder, default_org, run_job_from_playbook): + """Facts from a constructed inventory job must be saved to the source host. + + Generated by Claude Opus 4.6 (claude-opus-4-6). + + Constructed inventories contain hosts that are references (via instance_id) + to 'real' hosts in input inventories. start_fact_cache correctly resolves + source hosts via get_hosts_for_fact_cache(), but finish_fact_cache must also + write facts back to the source hosts, not the constructed inventory's copies. + + Scenario: + - Two input inventories each have a host named 'ci_shared_host' + - A constructed inventory uses both as inputs + - The inventory sync picks one source host (via instance_id) for the + constructed host — which one depends on input processing order + - Both source hosts start with distinct pre-existing facts + - A fact-gathering job runs against the constructed inventory + - After completion, the targeted source host should have the job's facts + - The OTHER source host must retain its original facts untouched + - The constructed host itself must NOT have facts stored on it + (constructed hosts are transient — recreated on each inventory sync) + """ + shared_name = 'ci_shared_host' + + # Cleanup from prior runs + for inv_name in ('test_ci_facts_input1', 'test_ci_facts_input2', 'test_ci_facts_constructed'): + Inventory.objects.filter(name=inv_name).delete() + + # --- Create two input inventories, each with an identically-named host --- + inv1 = Inventory.objects.create(organization=default_org, name='test_ci_facts_input1') + source_host1 = inv1.hosts.create(name=shared_name) + + inv2 = Inventory.objects.create(organization=default_org, name='test_ci_facts_input2') + source_host2 = inv2.hosts.create(name=shared_name) + + # Give both hosts distinct pre-existing facts so we can detect cross-contamination + host1_original_facts = {'source': 'inventory_1'} + source_host1.ansible_facts = host1_original_facts + source_host1.ansible_facts_modified = now() + source_host1.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + host2_original_facts = {'source': 'inventory_2'} + source_host2.ansible_facts = host2_original_facts + source_host2.ansible_facts_modified = now() + source_host2.save(update_fields=['ansible_facts', 'ansible_facts_modified']) + + source_hosts_by_id = {source_host1.id: source_host1, source_host2.id: source_host2} + original_facts_by_id = {source_host1.id: host1_original_facts, source_host2.id: host2_original_facts} + + # --- Create constructed inventory (sync will create hosts from inputs) --- + constructed_inv = Inventory.objects.create( + organization=default_org, + name='test_ci_facts_constructed', + kind='constructed', + ) + constructed_inv.input_inventories.add(inv1) + constructed_inv.input_inventories.add(inv2) + + # --- Run a fact-gathering job against the constructed inventory --- + # The job launch triggers an inventory sync which creates the constructed + # host with instance_id pointing to one of the source hosts. + scm_url = f'file://{live_tmp_folder}/facts' + run_job_from_playbook( + 'test_ci_facts', + 'gather.yml', + scm_url=scm_url, + jt_params={'use_fact_cache': True, 'inventory': constructed_inv.id}, + ) + + # --- Determine which source host the constructed host points to --- + constructed_host = constructed_inv.hosts.get(name=shared_name) + target_id = int(constructed_host.instance_id) + other_id = (set(source_hosts_by_id.keys()) - {target_id}).pop() + + target_host = source_hosts_by_id[target_id] + other_host = source_hosts_by_id[other_id] + + target_host.refresh_from_db() + other_host.refresh_from_db() + constructed_host.refresh_from_db() + + actual = [target_host.ansible_facts.get('foo'), other_host.ansible_facts, constructed_host.ansible_facts] + expected = ['bar', original_facts_by_id[other_id], {}] + assert actual == expected, ( + f'Constructed inventory fact cache wrote to wrong host(s). ' + f'target source host (id={target_id}) foo={actual[0]!r}, ' + f'other source host (id={other_id}) facts={actual[1]!r}, ' + f'constructed host facts={actual[2]!r}; expected {expected!r}' + ) diff --git a/awx/main/tests/live/tests/test_demo_data.py b/awx/main/tests/live/tests/test_demo_data.py new file mode 100644 index 000000000000..fa9ee5eb978c --- /dev/null +++ b/awx/main/tests/live/tests/test_demo_data.py @@ -0,0 +1,15 @@ +from awx.api.versioning import reverse + +from awx.main.models import JobTemplate, Job + +from awx.main.tests.live.tests.conftest import wait_for_job + + +def test_launch_demo_jt(post, admin): + jt = JobTemplate.objects.get(name='Demo Job Template') + + url = reverse('api:job_template_launch', kwargs={'pk': jt.id}) + + r = post(url=url, data={}, user=admin, expect=201) + job = Job.objects.get(pk=r.data['id']) + wait_for_job(job) diff --git a/awx/main/tests/live/tests/test_devel_image.py b/awx/main/tests/live/tests/test_devel_image.py new file mode 100644 index 000000000000..a77039247cc3 --- /dev/null +++ b/awx/main/tests/live/tests/test_devel_image.py @@ -0,0 +1,10 @@ +import os + +RSYSLOG_CONFIG = '/var/lib/awx/rsyslog/rsyslog.conf' + + +def test_rsyslog_config_readable(): + with open(RSYSLOG_CONFIG, 'r') as f: + content = f.read() + assert '/var/lib/awx/rsyslog' in content + assert oct(os.stat(RSYSLOG_CONFIG).st_mode) == '0o100640' diff --git a/awx/main/tests/live/tests/test_host_update_contention.py b/awx/main/tests/live/tests/test_host_update_contention.py new file mode 100644 index 000000000000..d822fc27c11c --- /dev/null +++ b/awx/main/tests/live/tests/test_host_update_contention.py @@ -0,0 +1,78 @@ +import multiprocessing +import random + +from django.db import connection +from django.utils.timezone import now + +from awx.main.models import Inventory, Host +from awx.main.utils.db import bulk_update_sorted_by_id + + +def worker_delete_target(ready_event, continue_event, field_name): + """Runs the bulk update, will be called in duplicate, in parallel""" + inv = Inventory.objects.get(organization__name='Default', name='test_host_update_contention') + host_list = list(inv.hosts.all()) + # Using random.shuffle for non-security-critical shuffling in a test + random.shuffle(host_list) # NOSONAR + for i, host in enumerate(host_list): + setattr(host, field_name, f'my_var: {i}') + + # ready to do the bulk_update + print('worker has loaded all the hosts needed') + ready_event.set() + # wait for the coordination message + continue_event.wait() + + # NOTE: did not reproduce the bug without batch_size + bulk_update_sorted_by_id(Host, host_list, fields=[field_name], batch_size=100) + print('finished doing the bulk update in worker') + + +def test_host_update_contention(default_org): + inv_kwargs = dict(organization=default_org, name='test_host_update_contention') + + if Inventory.objects.filter(**inv_kwargs).exists(): + inv = Inventory.objects.get(**inv_kwargs).delete() + + inv = Inventory.objects.create(**inv_kwargs) + right_now = now() + hosts = [Host(inventory=inv, name=f'host-{i}', created=right_now, modified=right_now) for i in range(1000)] + print('bulk creating hosts') + Host.objects.bulk_create(hosts) + + # sanity check + for host in hosts: + assert not host.variables + + # Force our worker pool to make their own connection + connection.close() + + ready_events = [multiprocessing.Event() for _ in range(2)] + continue_event = multiprocessing.Event() + + print('spawning processes for concurrent bulk updates') + processes = [] + fields = ['variables', 'ansible_facts'] + for i in range(2): + p = multiprocessing.Process(target=worker_delete_target, args=(ready_events[i], continue_event, fields[i])) + processes.append(p) + p.start() + + # Assure both processes are connected and have loaded their host list + for e in ready_events: + print('waiting on subprocess ready event') + e.wait() + + # Begin the bulk_update queries + print('setting the continue event for the workers') + continue_event.set() + + # if a Deadloack happens it will probably be surfaced by result here + print('waiting on the workers to finish the bulk_update') + for p in processes: + p.join() + + print('checking workers have variables set') + for host in inv.hosts.all(): + assert host.variables.startswith('my_var:') + assert host.ansible_facts.startswith('my_var:') diff --git a/awx/main/tests/live/tests/test_indirect_counting_external_queries.py b/awx/main/tests/live/tests/test_indirect_counting_external_queries.py new file mode 100644 index 000000000000..6dd445cab255 --- /dev/null +++ b/awx/main/tests/live/tests/test_indirect_counting_external_queries.py @@ -0,0 +1,347 @@ +""" +Integration tests for external query file functionality (AAP-58470). + +Tests verify the end-to-end external query file workflow for indirect node +counting using real AWX job execution. A fixture-created vendor collection +at /var/lib/awx/vendor_collections/ provides external query files, simulating +what the build-time (AAP-58426) and deployment (AAP-58557) integrations will +provide once available. + +Test data: +- Collection 'demo.external' at various versions (no embedded query) +- External query files in mock redhat.indirect_accounting collection +""" + +import os +import shutil +import time +import yaml + +import pytest +from flags.state import enable_flag, disable_flag, flag_enabled + +from awx.main.tests.live.tests.conftest import wait_for_events, unified_job_stdout +from awx.main.tasks.host_indirect import save_indirect_host_entries +from awx.main.models.indirect_managed_node_audit import IndirectManagedNodeAudit +from awx.main.models.event_query import EventQuery +from awx.main.models import Job + +# --- Constants --- + +EXTERNAL_QUERY_JQ = '{name: .name, canonical_facts: {host_name: .direct_host_name}, facts: {device_type: .device_type}}' + +EXTERNAL_QUERY_CONTENT = yaml.dump( + {'demo.external.example': {'query': EXTERNAL_QUERY_JQ}}, + default_flow_style=False, +) + +# For precedence test: different jq (no device_type in facts) so we can detect which query was used +EXTERNAL_QUERY_FOR_DEMO_QUERY_JQ = '{name: .name, canonical_facts: {host_name: .direct_host_name}, facts: {}}' + +EXTERNAL_QUERY_FOR_DEMO_QUERY_CONTENT = yaml.dump( + {'demo.query.example': {'query': EXTERNAL_QUERY_FOR_DEMO_QUERY_JQ}}, + default_flow_style=False, +) + +VENDOR_COLLECTIONS_BASE = '/var/lib/awx/vendor_collections' + + +# --- Fixtures --- + + +@pytest.fixture +def enable_indirect_host_counting(): + """Enable FEATURE_INDIRECT_NODE_COUNTING_ENABLED flag for the test. + + Only creates a FlagState DB record if the flag isn't already enabled + (e.g. via development_defaults.py), to avoid UniqueViolation errors + and to avoid leaking state to other tests. + """ + flag_name = "FEATURE_INDIRECT_NODE_COUNTING_ENABLED" + was_enabled = flag_enabled(flag_name) + if not was_enabled: + enable_flag(flag_name) + yield + if not was_enabled: + disable_flag(flag_name) + + +@pytest.fixture +def vendor_collections_dir(): + """Set up mock redhat.indirect_accounting collection at /var/lib/awx/vendor_collections/. + + Creates the collection structure with external query files: + - demo.external.1.0.0.yml (exact match for v1.0.0) + - demo.external.1.1.0.yml (fallback target for v1.5.0) + - demo.query.0.0.1.yml (for precedence test with embedded-query collection) + """ + base = os.path.join(VENDOR_COLLECTIONS_BASE, 'ansible_collections', 'redhat', 'indirect_accounting') + queries_path = os.path.join(base, 'extensions', 'audit', 'external_queries') + meta_path = os.path.join(base, 'meta') + + os.makedirs(queries_path, exist_ok=True) + os.makedirs(meta_path, exist_ok=True) + + # galaxy.yml for valid collection structure + with open(os.path.join(base, 'galaxy.yml'), 'w') as f: + yaml.dump( + { + 'namespace': 'redhat', + 'name': 'indirect_accounting', + 'version': '1.0.0', + 'description': 'Test fixture for external query integration tests', + 'authors': ['AWX Tests'], + 'dependencies': {}, + }, + f, + ) + + # meta/runtime.yml + with open(os.path.join(meta_path, 'runtime.yml'), 'w') as f: + yaml.dump({'requires_ansible': '>=2.15.0'}, f) + + # External query files for demo.external collection + for version in ('1.0.0', '1.1.0'): + with open(os.path.join(queries_path, f'demo.external.{version}.yml'), 'w') as f: + f.write(EXTERNAL_QUERY_CONTENT) + + # External query file for demo.query collection (precedence test) + with open(os.path.join(queries_path, 'demo.query.0.0.1.yml'), 'w') as f: + f.write(EXTERNAL_QUERY_FOR_DEMO_QUERY_CONTENT) + + yield base + + # Cleanup: only remove the collection we created, not the entire vendor root + shutil.rmtree(base, ignore_errors=True) + + +@pytest.fixture(autouse=True) +def cleanup_test_data(): + """Clean up EventQuery and IndirectManagedNodeAudit records after each test.""" + yield + EventQuery.objects.filter(fqcn='demo.external').delete() + EventQuery.objects.filter(fqcn='demo.query').delete() + IndirectManagedNodeAudit.objects.filter(job__name__icontains='external_query').delete() + + +# --- Helpers --- + + +def run_external_query_job(run_job_from_playbook, live_tmp_folder, test_name, project_dir, jt_params=None): + """Run a job and return the Job object after waiting for indirect host processing.""" + scm_url = f'file://{live_tmp_folder}/{project_dir}' + run_job_from_playbook(test_name, 'run_task.yml', scm_url=scm_url, jt_params=jt_params) + + job = Job.objects.filter(name__icontains=test_name).order_by('-created').first() + assert job is not None, f'Job not found for test {test_name}' + wait_for_events(job) + + return job + + +def wait_for_indirect_processing(job, expect_records=True, timeout=5): + """Wait for indirect host processing to complete. + + Follows the same pattern as test_indirect_host_counting.py:53-72. + """ + # Ensure indirect host processing runs (wait_for_events already called by caller) + job.refresh_from_db() + if job.event_queries_processed is False: + save_indirect_host_entries.delay(job.id, wait_for_events=False) + + if expect_records: + # Poll for audit records to appear + for _ in range(20): + if IndirectManagedNodeAudit.objects.filter(job=job).exists(): + break + time.sleep(0.25) + else: + raise RuntimeError(f'No IndirectManagedNodeAudit records populated for job_id={job.id}') + else: + # For negative tests, wait a reasonable time to confirm no records appear + time.sleep(timeout) + job.refresh_from_db() + + +# --- AC8.1: External query populates IndirectManagedNodeAudit correctly --- + + +def test_external_query_populates_audit_table(live_tmp_folder, run_job_from_playbook, enable_indirect_host_counting, vendor_collections_dir): + """AC8.1: Job using demo.external.example with external query file populates + IndirectManagedNodeAudit table correctly. + + Uses demo.external v1.0.0 with exact-match external query file demo.external.1.0.0.yml. + """ + job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_1', + 'test_host_query_external_v1_0_0', + ) + wait_for_indirect_processing(job, expect_records=True) + + # Verify installed_collections captured demo.external + assert 'demo.external' in job.installed_collections + assert 'host_query' in job.installed_collections['demo.external'] + + # Verify IndirectManagedNodeAudit records + assert IndirectManagedNodeAudit.objects.filter(job=job).count() == 1 + host_audit = IndirectManagedNodeAudit.objects.filter(job=job).first() + + assert host_audit.canonical_facts == {'host_name': 'foo_host_default'} + assert host_audit.facts == {'device_type': 'Fake Host'} + assert host_audit.name == 'vm-foo' + assert host_audit.organization == job.organization + assert 'demo.external.example' in host_audit.events + + +# --- AC8.2: Precedence - embedded query takes precedence over external --- + + +def test_embedded_query_takes_precedence(live_tmp_folder, run_job_from_playbook, enable_indirect_host_counting, vendor_collections_dir): + """AC8.2: When collection has both embedded and external query files, + the embedded query takes precedence. + + Uses demo.query v0.0.1 which HAS an embedded query (extensions/audit/event_query.yml). + An external query (demo.query.0.0.1.yml) also exists but uses a different jq expression + (no device_type in facts). By checking the audit record's facts, we verify which query was used. + """ + # Run with demo.query collection (has embedded query) + job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_2', + 'test_host_query', + ) + wait_for_indirect_processing(job, expect_records=True) + + # Verify the embedded query was used (includes device_type in facts) + host_audit = IndirectManagedNodeAudit.objects.filter(job=job).first() + assert host_audit.facts == {'device_type': 'Fake Host'}, ( + 'Expected embedded query output (with device_type). ' 'If facts is {}, the external query was incorrectly used instead.' + ) + + +# --- AC8.3: Version fallback to compatible version --- + + +def test_fallback_to_compatible_version(live_tmp_folder, run_job_from_playbook, enable_indirect_host_counting, vendor_collections_dir): + """AC8.3: Job using collection version with no exact query file falls back + correctly to compatible version. + + Uses demo.external v1.5.0. No demo.external.1.5.0.yml exists, but + demo.external.1.1.0.yml is available (same major version, highest <= 1.5.0). + The fallback should find and use the 1.1.0 query. + """ + job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_3', + 'test_host_query_external_v1_5_0', + ) + wait_for_indirect_processing(job, expect_records=True) + + # Verify installed_collections captured demo.external at v1.5.0 + assert 'demo.external' in job.installed_collections + assert job.installed_collections['demo.external']['version'] == '1.5.0' + + # Verify IndirectManagedNodeAudit records were created via fallback + assert IndirectManagedNodeAudit.objects.filter(job=job).count() == 1 + host_audit = IndirectManagedNodeAudit.objects.filter(job=job).first() + + assert host_audit.canonical_facts == {'host_name': 'foo_host_default'} + assert host_audit.facts == {'device_type': 'Fake Host'} + assert host_audit.name == 'vm-foo' + + +# --- AC8.4: Fallback queries don't overcount --- + + +def test_fallback_does_not_overcount(live_tmp_folder, run_job_from_playbook, enable_indirect_host_counting, vendor_collections_dir): + """AC8.4: Fallback queries don't count MORE nodes than exact-version queries. + + Runs two jobs: + 1. Exact match scenario (demo.external v1.0.0 -> demo.external.1.0.0.yml) + 2. Fallback scenario (demo.external v1.5.0 -> falls back to demo.external.1.1.0.yml) + + Verifies that fallback record count <= exact record count. + """ + # Run exact-match job + exact_job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_4_exact', + 'test_host_query_external_v1_0_0', + ) + wait_for_indirect_processing(exact_job, expect_records=True) + exact_count = IndirectManagedNodeAudit.objects.filter(job=exact_job).count() + + # Run fallback job + fallback_job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_4_fallback', + 'test_host_query_external_v1_5_0', + ) + wait_for_indirect_processing(fallback_job, expect_records=True) + fallback_count = IndirectManagedNodeAudit.objects.filter(job=fallback_job).count() + + # Critical safety check: fallback must never count MORE than exact + assert fallback_count <= exact_count, ( + f'Overcounting detected! Fallback produced {fallback_count} records ' f'but exact match produced only {exact_count} records.' + ) + + # Both use the same jq expression and same module, so counts should be equal + assert exact_count == fallback_count + + +# --- AC8.5: Warning logs contain correct version information --- + + +def test_fallback_log_contains_version_info(live_tmp_folder, run_job_from_playbook, enable_indirect_host_counting, vendor_collections_dir): + """AC8.5: Warning logs contain correct version information when fallback is used. + + Runs a job with verbosity=1 so callback plugin verbose output is captured. + Verifies the log contains the installed version (1.5.0), fallback version (1.1.0), + and collection FQCN (demo.external). + """ + job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_5', + 'test_host_query_external_v1_5_0', + jt_params={'verbosity': 1}, + ) + wait_for_indirect_processing(job, expect_records=True) + + # Get job stdout to check for fallback log message + stdout = unified_job_stdout(job) + + # The callback plugin emits: "Using external query {version_used} for {fqcn} v{ver}." + assert '1.1.0' in stdout, f'Fallback version 1.1.0 not found in job stdout. stdout:\n{stdout}' + assert 'demo.external' in stdout, f'Collection FQCN demo.external not found in job stdout. stdout:\n{stdout}' + assert '1.5.0' in stdout, f'Installed version 1.5.0 not found in job stdout. stdout:\n{stdout}' + + +# --- AC8.6: No counting when no compatible fallback exists --- + + +def test_no_counting_without_compatible_fallback(live_tmp_folder, run_job_from_playbook, enable_indirect_host_counting, vendor_collections_dir): + """AC8.6: No counting occurs when no compatible fallback exists. + + Uses demo.external v3.0.0 with only v1.x external query files available. + Since major versions differ (3 vs 1), no fallback should occur and no + IndirectManagedNodeAudit records should be created. + """ + job = run_external_query_job( + run_job_from_playbook, + live_tmp_folder, + 'external_query_ac8_6', + 'test_host_query_external_v3_0_0', + ) + wait_for_indirect_processing(job, expect_records=False) + + # No audit records should exist for this job + assert IndirectManagedNodeAudit.objects.filter(job=job).count() == 0, ( + 'IndirectManagedNodeAudit records were created despite no compatible ' 'fallback existing for demo.external v3.0.0 (only v1.x queries available).' + ) diff --git a/awx/main/tests/live/tests/test_indirect_host_counting.py b/awx/main/tests/live/tests/test_indirect_host_counting.py new file mode 100644 index 000000000000..2843eaeba535 --- /dev/null +++ b/awx/main/tests/live/tests/test_indirect_host_counting.py @@ -0,0 +1,66 @@ +import yaml +import time + +from awx.main.tests.live.tests.conftest import wait_for_events +from awx.main.tasks.host_indirect import build_indirect_host_data, save_indirect_host_entries +from awx.main.models.indirect_managed_node_audit import IndirectManagedNodeAudit +from awx.main.models import Job + + +def test_indirect_host_counting(live_tmp_folder, run_job_from_playbook): + run_job_from_playbook('test_indirect_host_counting', 'run_task.yml', scm_url=f'file://{live_tmp_folder}/test_host_query') + job = Job.objects.filter(name__icontains='test_indirect_host_counting').order_by('-created').first() + wait_for_events(job) # We must wait for events because system tasks iterate on job.job_events.filter(...) + + # Data matches to awx/main/tests/data/projects/host_query/extensions/audit/event_query.yml + # this just does things in-line to be a more localized test for the immediate testing + module_jq_str = '{name: .name, canonical_facts: {host_name: .direct_host_name}, facts: {device_type: .device_type}}' + event_query = {'demo.query.example': {'query': module_jq_str}} + + # Run the task logic directly with local data + results = build_indirect_host_data(job, event_query) + assert len(results) == 1 + host_audit_entry = results[0] + + canonical_facts = {'host_name': 'foo_host_default'} + facts = {'device_type': 'Fake Host'} + + # Asserts on data that will match to the input jq string from above + assert host_audit_entry.canonical_facts == canonical_facts + assert host_audit_entry.facts == facts + + # Test collection of data + assert 'demo.query' in job.installed_collections + assert 'host_query' in job.installed_collections['demo.query'] + hq_text = job.installed_collections['demo.query']['host_query'] + hq_data = yaml.safe_load(hq_text) + assert hq_data == {'demo.query.example': {'query': module_jq_str}} + + assert job.ansible_version + + # Poll for events finishing processing, because background task requires this + for _ in range(10): + if job.job_events.count() >= job.emitted_events: + break + time.sleep(0.2) + else: + raise RuntimeError(f'job id={job.id} never processed events') + + # Task might not run due to race condition, so make it run here + job.refresh_from_db() + if job.event_queries_processed is False: + save_indirect_host_entries.delay(job.id, wait_for_events=False) + + # event_queries_processed only assures the task has started, it might take a minor amount of time to finish + for _ in range(10): + if IndirectManagedNodeAudit.objects.filter(job=job).exists(): + break + time.sleep(0.2) + else: + raise RuntimeError(f'No IndirectManagedNodeAudit records ever populated for job_id={job.id}') + + assert IndirectManagedNodeAudit.objects.filter(job=job).count() == 1 + host_audit = IndirectManagedNodeAudit.objects.filter(job=job).first() + assert host_audit.canonical_facts == canonical_facts + assert host_audit.facts == facts + assert host_audit.organization == job.organization diff --git a/awx/main/tests/live/tests/test_inventory_vars.py b/awx/main/tests/live/tests/test_inventory_vars.py new file mode 100644 index 000000000000..611a8fffd92d --- /dev/null +++ b/awx/main/tests/live/tests/test_inventory_vars.py @@ -0,0 +1,223 @@ +import subprocess +import time +import os.path +from urllib.parse import urlsplit + +import pytest +from unittest import mock + +from awx.main.models.projects import Project +from awx.main.models.organization import Organization +from awx.main.models.inventory import Inventory, InventorySource +from awx.main.tests.live.tests.conftest import wait_for_job + +NAME_PREFIX = "test-ivu" +GIT_REPO_FOLDER = "inventory_vars" + + +def create_new_by_name(model, **kwargs): + """ + Create a new model instance. Delete an existing instance first. + + :param model: The Django model. + :param dict kwargs: The keyword arguments required to create a model + instance. Must contain at least `name`. + :return: The model instance. + """ + name = kwargs["name"] + try: + instance = model.objects.get(name=name) + except model.DoesNotExist: + pass + else: + print(f"FORCE DELETE {name}") + instance.delete() + finally: + instance = model.objects.create(**kwargs) + return instance + + +def wait_for_update(instance, timeout=3.0): + """Wait until the last update of *instance* is finished.""" + start = time.time() + while time.time() - start < timeout: + if instance.current_job or instance.last_job or instance.last_job_run: + break + time.sleep(0.2) + assert instance.current_job or instance.last_job or instance.last_job_run, f'Instance never updated id={instance.id}' + update = instance.current_job or instance.last_job + if update: + wait_for_job(update) + + +def change_source_vars_and_update(invsrc, group_vars): + """ + Change the variables content of an inventory source and update its + inventory. + + Does not return before the inventory update is finished. + + :param invsrc: The inventory source instance. + :param dict group_vars: The variables for various groups. Format:: + + { + : {: , : , ..}, : + {: , : , ..}, .. + } + + :return: None + """ + project = invsrc.source_project + repo_path = urlsplit(project.scm_url).path + filepath = os.path.join(repo_path, invsrc.source_path) + # print(f"change_source_vars_and_update: {project=} {repo_path=} {filepath=}") + with open(filepath, "w") as fp: + for group, variables in group_vars.items(): + fp.write(f"[{group}:vars]\n") + for name, value in variables.items(): + fp.write(f"{name}={value}\n") + subprocess.run('git add .; git commit -m "Update variables in invsrc.source_path"', cwd=repo_path, shell=True) + # Update the project to sync the changed repo contents. + project.update() + wait_for_update(project) + # Update the inventory from the changed source. + invsrc.update() + wait_for_update(invsrc) + + +@pytest.fixture +def organization(): + name = f"{NAME_PREFIX}-org" + instance = create_new_by_name(Organization, name=name, description=f"Description for {name}") + yield instance + instance.delete() + + +@pytest.fixture +def project(organization, live_tmp_folder): + name = f"{NAME_PREFIX}-project" + instance = create_new_by_name( + Project, + name=name, + description=f"Description for {name}", + organization=organization, + scm_url=f"file://{live_tmp_folder}/{GIT_REPO_FOLDER}", + scm_type="git", + ) + yield instance + instance.delete() + + +@pytest.fixture +def inventory(organization): + name = f"{NAME_PREFIX}-inventory" + instance = create_new_by_name( + Inventory, + name=name, + description=f"Description for {name}", + organization=organization, + ) + yield instance + instance.delete() + + +@pytest.fixture +def inventory_source(inventory, project): + name = f"{NAME_PREFIX}-invsrc" + inv_src = InventorySource( + name=name, + source_project=project, + source="scm", + source_path="inventory_var_deleted_in_source.ini", + inventory=inventory, + overwrite_vars=True, + ) + with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): + inv_src.save() + yield inv_src + inv_src.delete() + + +@pytest.fixture +def inventory_source_factory(inventory, project): + """ + Use this fixture if you want to use multiple inventory sources for the same + inventory in your test. + """ + # https://docs.pytest.org/en/stable/how-to/fixtures.html#factories-as-fixtures + + created = [] + # repo_path = f"{live_tmp_folder}/{GIT_REPO_FOLDER}" + + def _factory(inventory_file, name): + # Make sure the inventory file exists before the inventory source + # instance is created. + # + # Note: The current implementation of the inventory source object allows + # to create an instance even when the inventory source file does not + # exist. If this behaviour changes, uncomment the following code block + # and add the fixture `live_tmp_folder` to the factory function + # signature. + # + # inventory_file_path = os.path.join(repo_path, inventory_file) if not + # os.path.isfile(inventory_file_path): with open(inventory_file_path, + # "w") as fp: pass subprocess.run(f'git add .; git commit -m "Create + # {inventory_file_path}"', cwd=repo_path, shell=True) + # + # Create the inventory source instance. + name = f"{NAME_PREFIX}-invsrc-{name}" + inv_src = InventorySource( + name=name, + source_project=project, + source="scm", + source_path=inventory_file, + inventory=inventory, + overwrite_vars=True, + ) + with mock.patch('awx.main.models.unified_jobs.UnifiedJobTemplate.update'): + inv_src.save() + return inv_src + + yield _factory + for instance in created: + instance.delete() + + +def test_inventory_var_deleted_in_source(inventory, inventory_source): + """ + Verify that a variable which is deleted from its (git-)source between two + updates is also deleted from the inventory. + + Verifies https://issues.redhat.com/browse/AAP-17690 + """ + inventory_source.update() + wait_for_update(inventory_source) + assert {"a": "value_a", "b": "value_b"} == Inventory.objects.get(name=inventory.name).variables_dict + # Remove variable `a` from source and verify that it is also removed from + # the inventory variables. + change_source_vars_and_update(inventory_source, {"all": {"b": "value_b"}}) + assert {"b": "value_b"} == Inventory.objects.get(name=inventory.name).variables_dict + + +def test_inventory_vars_with_multiple_sources(inventory, inventory_source_factory): + """ + Verify a sequence of updates from various sources with changing content. + """ + invsrc_a = inventory_source_factory("invsrc_a.ini", "A") + invsrc_b = inventory_source_factory("invsrc_b.ini", "B") + invsrc_c = inventory_source_factory("invsrc_c.ini", "C") + + change_source_vars_and_update(invsrc_a, {"all": {"x": "x_from_a", "y": "y_from_a"}}) + assert {"x": "x_from_a", "y": "y_from_a"} == Inventory.objects.get(name=inventory.name).variables_dict + change_source_vars_and_update(invsrc_b, {"all": {"x": "x_from_b", "y": "y_from_b", "z": "z_from_b"}}) + assert {"x": "x_from_b", "y": "y_from_b", "z": "z_from_b"} == Inventory.objects.get(name=inventory.name).variables_dict + change_source_vars_and_update(invsrc_c, {"all": {"x": "x_from_c", "z": "z_from_c"}}) + assert {"x": "x_from_c", "y": "y_from_b", "z": "z_from_c"} == Inventory.objects.get(name=inventory.name).variables_dict + change_source_vars_and_update(invsrc_b, {"all": {}}) + assert {"x": "x_from_c", "y": "y_from_a", "z": "z_from_c"} == Inventory.objects.get(name=inventory.name).variables_dict + change_source_vars_and_update(invsrc_c, {"all": {"z": "z_from_c"}}) + assert {"x": "x_from_a", "y": "y_from_a", "z": "z_from_c"} == Inventory.objects.get(name=inventory.name).variables_dict + change_source_vars_and_update(invsrc_a, {"all": {}}) + assert {"z": "z_from_c"} == Inventory.objects.get(name=inventory.name).variables_dict + change_source_vars_and_update(invsrc_c, {"all": {}}) + assert {} == Inventory.objects.get(name=inventory.name).variables_dict diff --git a/awx/main/tests/live/tests/test_job_cancel.py b/awx/main/tests/live/tests/test_job_cancel.py new file mode 100644 index 000000000000..6a88d4b9a8e1 --- /dev/null +++ b/awx/main/tests/live/tests/test_job_cancel.py @@ -0,0 +1,40 @@ +import time + +from awx.api.versioning import reverse +from awx.main.models import Job + +from awx.main.tests.live.tests.conftest import wait_for_events + + +def test_cancel_and_delete_job(live_tmp_folder, run_job_from_playbook, post, delete, admin): + res = run_job_from_playbook('test_cancel_and_delete_job', 'sleep.yml', scm_url=f'file://{live_tmp_folder}/debug', wait=False) + job = res['job'] + assert job.status == 'pending' + + # Wait for first event so that we can be sure the job is in-progress first + start = time.time() + timeout = 10.0 + while not job.job_events.exists(): + time.sleep(0.2) + if time.time() - start > timeout: + assert False, f'Did not receive first event for job_id={job.id} in {timeout} seconds' + + # Now cancel the job + url = reverse("api:job_cancel", kwargs={'pk': job.pk}) + post(url, user=admin, expect=202) + + # Job status should change to expected status before infinity + start = time.time() + timeout = 5.0 + job.refresh_from_db() + while job.status != 'canceled': + time.sleep(0.05) + job.refresh_from_db(fields=['status']) + if time.time() - start > timeout: + assert False, f'job_id={job.id} still status={job.status} after {timeout} seconds' + + wait_for_events(job) + url = reverse("api:job_detail", kwargs={'pk': job.pk}) + delete(url, user=admin, expect=204) + + assert not Job.objects.filter(id=job.id).exists() diff --git a/awx/main/tests/live/tests/test_partitions.py b/awx/main/tests/live/tests/test_partitions.py new file mode 100644 index 000000000000..f395eda8e13e --- /dev/null +++ b/awx/main/tests/live/tests/test_partitions.py @@ -0,0 +1,23 @@ +from datetime import timedelta + +from django.utils.timezone import now +from django.db import connection + +from awx.main.utils.common import create_partition, table_exists + + +def test_table_when_it_exists(): + with connection.cursor() as cursor: + assert table_exists(cursor, 'main_job') + + +def test_table_when_it_does_not_exists(): + with connection.cursor() as cursor: + assert not table_exists(cursor, 'main_not_a_table_check') + + +def test_create_partition_race_condition(mocker): + mocker.patch('awx.main.utils.common.table_exists', return_value=False) + + create_partition('main_jobevent', start=now() - timedelta(days=2)) + create_partition('main_jobevent', start=now() - timedelta(days=2)) diff --git a/awx/main/tests/settings_for_test.py b/awx/main/tests/settings_for_test.py new file mode 100644 index 000000000000..5634494c3373 --- /dev/null +++ b/awx/main/tests/settings_for_test.py @@ -0,0 +1,23 @@ +# Python +import uuid + +# Load development settings for base variables. +from awx.settings.development import * # NOQA + +# Some things make decisions based on settings.SETTINGS_MODULE, so this is done for that +SETTINGS_MODULE = 'awx.settings.development' + +# Use SQLite for unit tests instead of PostgreSQL. If the lines below are +# commented out, Django will create the test_awx-dev database in PostgreSQL to +# run unit tests. +CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-{}'.format(str(uuid.uuid4()))}} +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'awx.sqlite3'), # noqa + 'TEST': { + # Test database cannot be :memory: for inventory tests. + 'NAME': os.path.join(BASE_DIR, 'awx_test.sqlite3') # noqa + }, + } +} diff --git a/awx/main/tests/unit/analytics/test_broadcast_websocket.py b/awx/main/tests/unit/analytics/test_broadcast_websocket.py index cd7f4323b5cc..9aa24e772b2a 100644 --- a/awx/main/tests/unit/analytics/test_broadcast_websocket.py +++ b/awx/main/tests/unit/analytics/test_broadcast_websocket.py @@ -1,6 +1,7 @@ import datetime +from unittest.mock import Mock, patch -from awx.main.analytics.broadcast_websocket import FixedSlidingWindow +from awx.main.analytics.broadcast_websocket import FixedSlidingWindow, RelayWebsocketStatsManager from awx.main.analytics.broadcast_websocket import dt_to_seconds @@ -59,3 +60,70 @@ def test_record_same_minute_render_diff_minute(self): assert 20 - i == fsw.render(self.ts(minute=1, second=i, microsecond=0)), "E. Sliding window where 1 record() should drop from the results each time" assert 0 == fsw.render(self.ts(minute=1, second=20, microsecond=0)), "F. First second one minute after all record() calls" + + +class TestRelayWebsocketStatsManager: + """Test Redis client caching in RelayWebsocketStatsManager.""" + + def test_get_stats_sync_caches_redis_client(self): + """Verify get_stats_sync caches Redis client to avoid creating new connection pools.""" + # Reset class variable + RelayWebsocketStatsManager._redis_client = None + + mock_redis = Mock() + mock_redis.get.return_value = b'' + + with patch('awx.main.analytics.broadcast_websocket.get_redis_client', return_value=mock_redis) as mock_get_client: + # First call should create client + RelayWebsocketStatsManager.get_stats_sync() + assert mock_get_client.call_count == 1 + + # Second call should reuse cached client + RelayWebsocketStatsManager.get_stats_sync() + assert mock_get_client.call_count == 1 # Still 1, not called again + + # Third call should still reuse cached client + RelayWebsocketStatsManager.get_stats_sync() + assert mock_get_client.call_count == 1 + + # Cleanup + RelayWebsocketStatsManager._redis_client = None + + def test_get_stats_sync_returns_parsed_metrics(self): + """Verify get_stats_sync returns parsed metric families from Redis.""" + # Reset class variable + RelayWebsocketStatsManager._redis_client = None + + # Sample Prometheus metrics format + sample_metrics = b'# HELP test_metric A test metric\n# TYPE test_metric gauge\ntest_metric 42\n' + + mock_redis = Mock() + mock_redis.get.return_value = sample_metrics + + with patch('awx.main.analytics.broadcast_websocket.get_redis_client', return_value=mock_redis): + result = list(RelayWebsocketStatsManager.get_stats_sync()) + + # Should return parsed metric families + assert len(result) > 0 + assert mock_redis.get.called + + # Cleanup + RelayWebsocketStatsManager._redis_client = None + + def test_get_stats_sync_handles_empty_redis_data(self): + """Verify get_stats_sync handles empty data from Redis gracefully.""" + # Reset class variable + RelayWebsocketStatsManager._redis_client = None + + mock_redis = Mock() + mock_redis.get.return_value = None # Redis returns None when key doesn't exist + + with patch('awx.main.analytics.broadcast_websocket.get_redis_client', return_value=mock_redis): + result = list(RelayWebsocketStatsManager.get_stats_sync()) + + # Should handle empty data gracefully + assert result == [] + assert mock_redis.get.called + + # Cleanup + RelayWebsocketStatsManager._redis_client = None diff --git a/awx/main/tests/unit/api/serializers/test_job_template_serializers.py b/awx/main/tests/unit/api/serializers/test_job_template_serializers.py index 51e64fd753a1..0a9e31b91cbd 100644 --- a/awx/main/tests/unit/api/serializers/test_job_template_serializers.py +++ b/awx/main/tests/unit/api/serializers/test_job_template_serializers.py @@ -76,15 +76,15 @@ def test_callback_absent(self, get_related_mock_and_run, job_template): class TestJobTemplateSerializerGetSummaryFields: def test_survey_spec_exists(self, test_get_summary_fields, mocker, job_template): job_template.survey_spec = {'name': 'blah', 'description': 'blah blah'} - with mocker.patch.object(JobTemplateSerializer, '_recent_jobs') as mock_rj: - mock_rj.return_value = [] - test_get_summary_fields(JobTemplateSerializer, job_template, 'survey') + mock_rj = mocker.patch.object(JobTemplateSerializer, '_recent_jobs') + mock_rj.return_value = [] + test_get_summary_fields(JobTemplateSerializer, job_template, 'survey') def test_survey_spec_absent(self, get_summary_fields_mock_and_run, mocker, job_template): job_template.survey_spec = None - with mocker.patch.object(JobTemplateSerializer, '_recent_jobs') as mock_rj: - mock_rj.return_value = [] - summary = get_summary_fields_mock_and_run(JobTemplateSerializer, job_template) + mock_rj = mocker.patch.object(JobTemplateSerializer, '_recent_jobs') + mock_rj.return_value = [] + summary = get_summary_fields_mock_and_run(JobTemplateSerializer, job_template) assert 'survey' not in summary def test_copy_edit_standard(self, mocker, job_template_factory): @@ -107,10 +107,10 @@ def test_copy_edit_standard(self, mocker, job_template_factory): view.kwargs = {} serializer.context['view'] = view - with mocker.patch("awx.api.serializers.role_summary_fields_generator", return_value='Can eat pie'): - with mocker.patch("awx.main.access.JobTemplateAccess.can_change", return_value='foobar'): - with mocker.patch("awx.main.access.JobTemplateAccess.can_copy", return_value='foo'): - response = serializer.get_summary_fields(jt_obj) + mocker.patch("awx.api.serializers.role_summary_fields_generator", return_value='Can eat pie') + mocker.patch("awx.main.access.JobTemplateAccess.can_change", return_value='foobar') + mocker.patch("awx.main.access.JobTemplateAccess.can_copy", return_value='foo') + response = serializer.get_summary_fields(jt_obj) assert response['user_capabilities']['copy'] == 'foo' assert response['user_capabilities']['edit'] == 'foobar' diff --git a/awx/main/tests/unit/api/serializers/test_token_serializer.py b/awx/main/tests/unit/api/serializers/test_token_serializer.py deleted file mode 100644 index aa6363d47a10..000000000000 --- a/awx/main/tests/unit/api/serializers/test_token_serializer.py +++ /dev/null @@ -1,8 +0,0 @@ -import pytest - -from awx.api.serializers import OAuth2TokenSerializer - - -@pytest.mark.parametrize('scope, expect', [('', False), ('read', True), ('read read', False), ('write read', True), ('read rainbow', False)]) -def test_invalid_scopes(scope, expect): - assert OAuth2TokenSerializer()._is_valid_scope(scope) is expect diff --git a/awx/main/tests/unit/api/serializers/test_unified_serializers.py b/awx/main/tests/unit/api/serializers/test_unified_serializers.py index 36558f92cb4f..47451d849a03 100644 --- a/awx/main/tests/unit/api/serializers/test_unified_serializers.py +++ b/awx/main/tests/unit/api/serializers/test_unified_serializers.py @@ -39,7 +39,7 @@ def test_unified_job_detail_exclusive_fields(): For each type, assert that the only fields allowed to be exclusive to detail view are the allowed types """ - allowed_detail_fields = frozenset(('result_traceback', 'job_args', 'job_cwd', 'job_env', 'event_processing_finished')) + allowed_detail_fields = frozenset(('result_traceback', 'job_args', 'job_cwd', 'job_env', 'event_processing_finished', 'artifacts')) for cls in UnifiedJob.__subclasses__(): list_serializer = getattr(serializers, '{}ListSerializer'.format(cls.__name__)) detail_serializer = getattr(serializers, '{}Serializer'.format(cls.__name__)) diff --git a/awx/main/tests/unit/api/serializers/test_workflow_serializers.py b/awx/main/tests/unit/api/serializers/test_workflow_serializers.py index 9e7fe51344e0..218395dc6c62 100644 --- a/awx/main/tests/unit/api/serializers/test_workflow_serializers.py +++ b/awx/main/tests/unit/api/serializers/test_workflow_serializers.py @@ -189,8 +189,8 @@ def test_use_db_answer(self, jt, mocker): serializer = WorkflowJobTemplateNodeSerializer() wfjt = WorkflowJobTemplate.objects.create(name='fake-wfjt') serializer.instance = WorkflowJobTemplateNode(workflow_job_template=wfjt, unified_job_template=jt, extra_data={'var1': '$encrypted$foooooo'}) - with mocker.patch('awx.main.models.mixins.decrypt_value', return_value='foo'): - attrs = serializer.validate({'unified_job_template': jt, 'workflow_job_template': wfjt, 'extra_data': {'var1': '$encrypted$'}}) + mocker.patch('awx.main.models.mixins.decrypt_value', return_value='foo') + attrs = serializer.validate({'unified_job_template': jt, 'workflow_job_template': wfjt, 'extra_data': {'var1': '$encrypted$'}}) assert 'survey_passwords' in attrs assert 'var1' in attrs['survey_passwords'] assert attrs['extra_data']['var1'] == '$encrypted$foooooo' diff --git a/awx/main/tests/unit/api/test_fields.py b/awx/main/tests/unit/api/test_fields.py new file mode 100644 index 000000000000..d6b6ae49d266 --- /dev/null +++ b/awx/main/tests/unit/api/test_fields.py @@ -0,0 +1,49 @@ +import pytest +from collections import OrderedDict +from unittest import mock + +from rest_framework.exceptions import ValidationError + +from awx.api.fields import DeprecatedCredentialField + + +class TestDeprecatedCredentialField: + """Test that DeprecatedCredentialField handles unexpected input types gracefully.""" + + def test_dict_value_raises_validation_error(self): + """Passing a dict instead of an integer should return a 400 validation error, not a 500 TypeError.""" + field = DeprecatedCredentialField() + with pytest.raises(ValidationError): + field.to_internal_value({"username": "admin", "password": "secret"}) + + def test_ordered_dict_value_raises_validation_error(self): + """Passing an OrderedDict should return a 400 validation error, not a 500 TypeError.""" + field = DeprecatedCredentialField() + with pytest.raises(ValidationError): + field.to_internal_value(OrderedDict([("username", "admin")])) + + def test_list_value_raises_validation_error(self): + """Passing a list should return a 400 validation error, not a 500 TypeError.""" + field = DeprecatedCredentialField() + with pytest.raises(ValidationError): + field.to_internal_value([1, 2, 3]) + + def test_string_value_raises_validation_error(self): + """Passing a non-numeric string should return a 400 validation error.""" + field = DeprecatedCredentialField() + with pytest.raises(ValidationError): + field.to_internal_value("not_a_number") + + @mock.patch('awx.api.fields.Credential.objects') + def test_valid_integer_value_works(self, mock_cred_objects): + """Passing a valid integer PK should work when the credential exists.""" + mock_cred_objects.get.return_value = mock.MagicMock() + field = DeprecatedCredentialField() + assert field.to_internal_value(42) == 42 + + @mock.patch('awx.api.fields.Credential.objects') + def test_valid_string_integer_value_works(self, mock_cred_objects): + """Passing a numeric string PK should work when the credential exists.""" + mock_cred_objects.get.return_value = mock.MagicMock() + field = DeprecatedCredentialField() + assert field.to_internal_value("42") == 42 diff --git a/awx/main/tests/unit/api/test_filters.py b/awx/main/tests/unit/api/test_filters.py index 7d6501a87161..78cc7401cf15 100644 --- a/awx/main/tests/unit/api/test_filters.py +++ b/awx/main/tests/unit/api/test_filters.py @@ -3,15 +3,12 @@ import pytest # Django -from django.core.exceptions import FieldDoesNotExist +from rest_framework.exceptions import PermissionDenied -from rest_framework.exceptions import PermissionDenied, ParseError +from ansible_base.rest_filters.rest_framework.field_lookup_backend import FieldLookupBackend -from awx.api.filters import FieldLookupBackend, OrderByBackend, get_field_from_path from awx.main.models import ( AdHocCommand, - ActivityStream, - Credential, Job, JobTemplate, SystemJob, @@ -20,94 +17,15 @@ WorkflowJob, WorkflowJobTemplate, WorkflowJobOptions, - InventorySource, - JobEvent, ) -from awx.main.models.oauth import OAuth2Application from awx.main.models.jobs import JobOptions -def test_related(): - field_lookup = FieldLookupBackend() - lookup = '__'.join(['inventory', 'organization', 'pk']) - field, new_lookup = field_lookup.get_field_from_lookup(InventorySource, lookup) - print(field) - print(new_lookup) - - -def test_invalid_filter_key(): - field_lookup = FieldLookupBackend() - # FieldDoesNotExist is caught and converted to ParseError by filter_queryset - with pytest.raises(FieldDoesNotExist) as excinfo: - field_lookup.value_to_python(JobEvent, 'event_data.task_action', 'foo') - assert 'has no field named' in str(excinfo) - - -def test_invalid_field_hop(): - with pytest.raises(ParseError) as excinfo: - get_field_from_path(Credential, 'organization__description__user') - assert 'No related model for' in str(excinfo) - - -def test_invalid_order_by_key(): - field_order_by = OrderByBackend() - with pytest.raises(ParseError) as excinfo: - [f for f in field_order_by._validate_ordering_fields(JobEvent, ('event_data.task_action',))] - assert 'has no field named' in str(excinfo) - - -@pytest.mark.parametrize(u"empty_value", [u'', '']) -def test_empty_in(empty_value): - field_lookup = FieldLookupBackend() - with pytest.raises(ValueError) as excinfo: - field_lookup.value_to_python(JobTemplate, 'project__name__in', empty_value) - assert 'empty value for __in' in str(excinfo.value) - - -@pytest.mark.parametrize(u"valid_value", [u'foo', u'foo,']) -def test_valid_in(valid_value): - field_lookup = FieldLookupBackend() - value, new_lookup, _ = field_lookup.value_to_python(JobTemplate, 'project__name__in', valid_value) - assert 'foo' in value - - -def test_invalid_field(): - invalid_field = u"ヽヾ" - field_lookup = FieldLookupBackend() - with pytest.raises(ValueError) as excinfo: - field_lookup.value_to_python(WorkflowJobTemplate, invalid_field, 'foo') - assert 'is not an allowed field name. Must be ascii encodable.' in str(excinfo.value) - - -def test_valid_iexact(): - field_lookup = FieldLookupBackend() - value, new_lookup, _ = field_lookup.value_to_python(JobTemplate, 'project__name__iexact', 'foo') - assert 'foo' in value - - -def test_invalid_iexact(): - field_lookup = FieldLookupBackend() - with pytest.raises(ValueError) as excinfo: - field_lookup.value_to_python(Job, 'id__iexact', '1') - assert 'is not a text field and cannot be filtered by case-insensitive search' in str(excinfo.value) - - -@pytest.mark.parametrize('lookup_suffix', ['', 'contains', 'startswith', 'in']) -@pytest.mark.parametrize('password_field', Credential.PASSWORD_FIELDS) -def test_filter_on_password_field(password_field, lookup_suffix): - field_lookup = FieldLookupBackend() - lookup = '__'.join(filter(None, [password_field, lookup_suffix])) - with pytest.raises(PermissionDenied) as excinfo: - field, new_lookup = field_lookup.get_field_from_lookup(Credential, lookup) - assert 'not allowed' in str(excinfo.value) - - @pytest.mark.parametrize( 'model, query', [ (User, 'password__icontains'), (User, 'settings__value__icontains'), - (User, 'main_oauth2accesstoken__token__gt'), (UnifiedJob, 'job_args__icontains'), (UnifiedJob, 'job_env__icontains'), (UnifiedJob, 'start_args__icontains'), @@ -119,8 +37,6 @@ def test_filter_on_password_field(password_field, lookup_suffix): (WorkflowJob, 'survey_passwords__icontains'), (JobTemplate, 'survey_spec__icontains'), (WorkflowJobTemplate, 'survey_spec__icontains'), - (ActivityStream, 'o_auth2_application__client_secret__gt'), - (OAuth2Application, 'grant__code__gt'), ], ) def test_filter_sensitive_fields_and_relations(model, query): @@ -128,10 +44,3 @@ def test_filter_sensitive_fields_and_relations(model, query): with pytest.raises(PermissionDenied) as excinfo: field, new_lookup = field_lookup.get_field_from_lookup(model, query) assert 'not allowed' in str(excinfo.value) - - -def test_looping_filters_prohibited(): - field_lookup = FieldLookupBackend() - with pytest.raises(ParseError) as loop_exc: - field_lookup.get_field_from_lookup(Job, 'job_events__job__job_events') - assert 'job_events' in str(loop_exc.value) diff --git a/awx/main/tests/unit/api/test_generics.py b/awx/main/tests/unit/api/test_generics.py index 6f0982bfd847..05cc72cc1935 100644 --- a/awx/main/tests/unit/api/test_generics.py +++ b/awx/main/tests/unit/api/test_generics.py @@ -50,7 +50,7 @@ def test_attach_validate_ok(self, mocker): mock_request = mocker.MagicMock(data=dict(id=1)) serializer = SubListCreateAttachDetachAPIView() - (sub_id, res) = serializer.attach_validate(mock_request) + sub_id, res = serializer.attach_validate(mock_request) assert sub_id == 1 assert res is None @@ -59,12 +59,12 @@ def test_attach_validate_invalid_type(self, mocker): mock_request = mocker.MagicMock(data=dict(id='foobar')) serializer = SubListCreateAttachDetachAPIView() - (sub_id, res) = serializer.attach_validate(mock_request) + sub_id, res = serializer.attach_validate(mock_request) assert type(res) is Response def test_attach_create_and_associate(self, mocker, get_object_or_400, parent_relationship_factory): - (serializer, mock_parent_relationship) = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife') + serializer, mock_parent_relationship = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife') create_return_value = mocker.MagicMock(status_code=status.HTTP_201_CREATED) serializer.create = mocker.Mock(return_value=create_return_value) @@ -75,7 +75,7 @@ def test_attach_create_and_associate(self, mocker, get_object_or_400, parent_rel mock_parent_relationship.wife.add.assert_called_with(get_object_or_400.return_value) def test_attach_associate_only(self, mocker, get_object_or_400, parent_relationship_factory): - (serializer, mock_parent_relationship) = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife') + serializer, mock_parent_relationship = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife') serializer.create = mocker.Mock(return_value=mocker.MagicMock()) mock_request = mocker.MagicMock(data=dict(id=1)) @@ -88,7 +88,7 @@ def test_unattach_validate_ok(self, mocker): mock_request = mocker.MagicMock(data=dict(id=1)) serializer = SubListCreateAttachDetachAPIView() - (sub_id, res) = serializer.unattach_validate(mock_request) + sub_id, res = serializer.unattach_validate(mock_request) assert sub_id == 1 assert res is None @@ -97,7 +97,7 @@ def test_unattach_validate_invalid_type(self, mocker): mock_request = mocker.MagicMock(data=dict(id='foobar')) serializer = SubListCreateAttachDetachAPIView() - (sub_id, res) = serializer.unattach_validate(mock_request) + sub_id, res = serializer.unattach_validate(mock_request) assert type(res) is Response @@ -105,13 +105,13 @@ def test_unattach_validate_missing_id(self, mocker): mock_request = mocker.MagicMock(data=dict()) serializer = SubListCreateAttachDetachAPIView() - (sub_id, res) = serializer.unattach_validate(mock_request) + sub_id, res = serializer.unattach_validate(mock_request) assert sub_id is None assert type(res) is Response def test_unattach_by_id_ok(self, mocker, parent_relationship_factory, get_object_or_400): - (serializer, mock_parent_relationship) = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife') + serializer, mock_parent_relationship = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife') mock_request = mocker.MagicMock() mock_sub = mocker.MagicMock(name="object to unattach") get_object_or_400.return_value = mock_sub @@ -191,16 +191,16 @@ def mock_view(self, parent=None): def test_parent_access_check_failed(self, mocker, mock_organization): mock_access = mocker.MagicMock(__name__='for logger', return_value=False) - with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access): - with pytest.raises(PermissionDenied): - self.mock_view(parent=mock_organization).check_permissions(self.mock_request()) - mock_access.assert_called_once_with(mock_organization) + mocker.patch('awx.main.access.BaseAccess.can_read', mock_access) + with pytest.raises(PermissionDenied): + self.mock_view(parent=mock_organization).check_permissions(self.mock_request()) + mock_access.assert_called_once_with(mock_organization) def test_parent_access_check_worked(self, mocker, mock_organization): mock_access = mocker.MagicMock(__name__='for logger', return_value=True) - with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access): - self.mock_view(parent=mock_organization).check_permissions(self.mock_request()) - mock_access.assert_called_once_with(mock_organization) + mocker.patch('awx.main.access.BaseAccess.can_read', mock_access) + self.mock_view(parent=mock_organization).check_permissions(self.mock_request()) + mock_access.assert_called_once_with(mock_organization) def test_related_search_reverse_FK_field(): diff --git a/awx/main/tests/unit/api/test_logger.py b/awx/main/tests/unit/api/test_logger.py index b56da87da1a7..a3da0d23b829 100644 --- a/awx/main/tests/unit/api/test_logger.py +++ b/awx/main/tests/unit/api/test_logger.py @@ -47,7 +47,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")', # noqa + 'action(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")', # noqa ] ), ), @@ -61,7 +61,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")', - 'action(type="omfwd" target="localhost" port="9000" protocol="udp" action.resumeRetryCount="-1" action.resumeInterval="5" template="awx")', # noqa + 'action(type="omfwd" target="localhost" port="9000" protocol="udp" action.resumeRetryCount="-1" action.resumeInterval="5" template="awx" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5")', # noqa ] ), ), @@ -75,7 +75,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")', - 'action(type="omfwd" target="localhost" port="9000" protocol="tcp" action.resumeRetryCount="-1" action.resumeInterval="5" template="awx")', # noqa + 'action(type="omfwd" target="localhost" port="9000" protocol="tcp" action.resumeRetryCount="-1" action.resumeInterval="5" template="awx" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5")', # noqa ] ), ), @@ -89,7 +89,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="443" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="443" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa ] ), ), @@ -103,7 +103,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="80" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="80" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa ] ), ), @@ -117,7 +117,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa ] ), ), @@ -131,7 +131,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa ] ), ), @@ -145,7 +145,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa ] ), ), @@ -159,7 +159,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="services/collector/event")', # noqa ] ), ), @@ -173,7 +173,7 @@ '\n'.join( [ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="endpoint5.collection.us2.sumologic.com" serverport="443" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" errorfile="/var/log/tower/rsyslog.err" restpath="receiver/v1/http/ZaVnC4dhaV0qoiETY0MrM3wwLoDgO1jFgjOxE6-39qokkj3LGtOroZ8wNaN2M6DtgYrJZsmSi4-36_Up5TbbN_8hosYonLKHSSOSKY845LuLZBCBwStrHQ==")', # noqa + 'action(type="omhttp" server="endpoint5.collection.us2.sumologic.com" serverport="443" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" action.resumeInterval="5" queue.spoolDirectory="/var/lib/awx" queue.filename="awx-external-logger-action-queue" queue.maxDiskSpace="1g" queue.maxFileSize="100m" queue.type="LinkedList" queue.saveOnShutdown="on" queue.syncqueuefiles="on" queue.checkpointInterval="1000" queue.size="131072" queue.highwaterMark="98304" queue.discardMark="117964" queue.discardSeverity="5" errorfile="/var/log/tower/rsyslog.err" restpath="receiver/v1/http/ZaVnC4dhaV0qoiETY0MrM3wwLoDgO1jFgjOxE6-39qokkj3LGtOroZ8wNaN2M6DtgYrJZsmSi4-36_Up5TbbN_8hosYonLKHSSOSKY845LuLZBCBwStrHQ==")', # noqa ] ), ), diff --git a/awx/main/tests/unit/api/test_schema.py b/awx/main/tests/unit/api/test_schema.py new file mode 100644 index 000000000000..83ade9e16cc9 --- /dev/null +++ b/awx/main/tests/unit/api/test_schema.py @@ -0,0 +1,424 @@ +import copy +import warnings +from unittest.mock import Mock, patch + +from rest_framework.permissions import IsAuthenticated + +from awx.api.schema import ( + CustomAutoSchema, + AuthenticatedSpectacularAPIView, + AuthenticatedSpectacularSwaggerView, + AuthenticatedSpectacularRedocView, + filter_credential_type_schema, +) + + +class TestCustomAutoSchema: + """Unit tests for CustomAutoSchema class.""" + + def test_get_tags_with_swagger_topic(self): + """Test get_tags returns swagger_topic when available.""" + view = Mock() + view.swagger_topic = 'custom_topic' + view.get_serializer = Mock(return_value=Mock()) + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + assert tags == ['Custom_Topic'] + + def test_get_tags_with_serializer_meta_model(self): + """Test get_tags returns model verbose_name_plural from serializer.""" + # Create a mock model with verbose_name_plural + mock_model = Mock() + mock_model._meta.verbose_name_plural = 'test models' + + # Create a mock serializer with Meta.model + mock_serializer = Mock() + mock_serializer.Meta.model = mock_model + + view = Mock(spec=[]) # View without swagger_topic + view.get_serializer = Mock(return_value=mock_serializer) + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + assert tags == ['Test Models'] + + def test_get_tags_with_view_model(self): + """Test get_tags returns model verbose_name_plural from view.""" + # Create a mock model with verbose_name_plural + mock_model = Mock() + mock_model._meta.verbose_name_plural = 'view models' + + view = Mock(spec=['model']) # View without swagger_topic or get_serializer + view.model = mock_model + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + assert tags == ['View Models'] + + def test_get_tags_without_get_serializer(self): + """Test get_tags when view doesn't have get_serializer method.""" + mock_model = Mock() + mock_model._meta.verbose_name_plural = 'test objects' + + view = Mock(spec=['model']) + view.model = mock_model + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + assert tags == ['Test Objects'] + + def test_get_tags_serializer_exception_with_warning(self): + """Test get_tags handles exception in get_serializer with warning.""" + mock_model = Mock() + mock_model._meta.verbose_name_plural = 'fallback models' + + view = Mock(spec=['get_serializer', 'model', '__class__']) + view.__class__.__name__ = 'TestView' + view.get_serializer = Mock(side_effect=Exception('Serializer error')) + view.model = mock_model + + schema = CustomAutoSchema() + schema.view = view + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + tags = schema.get_tags() + + # Check that a warning was raised + assert len(w) == 1 + assert 'TestView.get_serializer() raised an exception' in str(w[0].message) + + # Should still get tags from view.model + assert tags == ['Fallback Models'] + + def test_get_tags_serializer_without_meta_model(self): + """Test get_tags when serializer doesn't have Meta.model.""" + mock_serializer = Mock(spec=[]) # No Meta attribute + + view = Mock(spec=['get_serializer']) + view.__class__.__name__ = 'NoMetaView' + view.get_serializer = Mock(return_value=mock_serializer) + + schema = CustomAutoSchema() + schema.view = view + + with patch.object(CustomAutoSchema.__bases__[0], 'get_tags', return_value=['Default Tag']) as mock_super: + tags = schema.get_tags() + mock_super.assert_called_once() + assert tags == ['Default Tag'] + + def test_get_tags_fallback_to_super(self): + """Test get_tags falls back to parent class method.""" + view = Mock(spec=['get_serializer']) + view.get_serializer = Mock(return_value=Mock(spec=[])) + + schema = CustomAutoSchema() + schema.view = view + + with patch.object(CustomAutoSchema.__bases__[0], 'get_tags', return_value=['Super Tag']) as mock_super: + tags = schema.get_tags() + mock_super.assert_called_once() + assert tags == ['Super Tag'] + + def test_get_tags_empty_with_warning(self): + """Test get_tags returns 'api' fallback when no tags can be determined.""" + view = Mock(spec=['get_serializer']) + view.__class__.__name__ = 'EmptyView' + view.get_serializer = Mock(return_value=Mock(spec=[])) + + schema = CustomAutoSchema() + schema.view = view + + with patch.object(CustomAutoSchema.__bases__[0], 'get_tags', return_value=[]): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + tags = schema.get_tags() + + # Check that a warning was raised + assert len(w) == 1 + assert 'Could not determine tags for EmptyView' in str(w[0].message) + + # Should fallback to 'api' + assert tags == ['api'] + + def test_get_tags_swagger_topic_title_case(self): + """Test that swagger_topic is properly title-cased.""" + view = Mock() + view.swagger_topic = 'multi_word_topic' + view.get_serializer = Mock(return_value=Mock()) + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + assert tags == ['Multi_Word_Topic'] + + def test_is_deprecated_true(self): + """Test is_deprecated returns True when view has deprecated=True.""" + view = Mock() + view.deprecated = True + + schema = CustomAutoSchema() + schema.view = view + + assert schema.is_deprecated() is True + + def test_is_deprecated_false(self): + """Test is_deprecated returns False when view has deprecated=False.""" + view = Mock() + view.deprecated = False + + schema = CustomAutoSchema() + schema.view = view + + assert schema.is_deprecated() is False + + def test_is_deprecated_missing_attribute(self): + """Test is_deprecated returns False when view doesn't have deprecated attribute.""" + view = Mock(spec=[]) + + schema = CustomAutoSchema() + schema.view = view + + assert schema.is_deprecated() is False + + def test_get_tags_serializer_meta_without_model(self): + """Test get_tags when serializer has Meta but no model attribute.""" + mock_serializer = Mock() + mock_serializer.Meta = Mock(spec=[]) # Meta exists but no model + + mock_model = Mock() + mock_model._meta.verbose_name_plural = 'backup models' + + view = Mock(spec=['get_serializer', 'model']) + view.get_serializer = Mock(return_value=mock_serializer) + view.model = mock_model + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + # Should fall back to view.model + assert tags == ['Backup Models'] + + def test_get_tags_complex_scenario_exception_recovery(self): + """Test complex scenario where serializer fails but view.model exists.""" + mock_model = Mock() + mock_model._meta.verbose_name_plural = 'recovery models' + + view = Mock(spec=['get_serializer', 'model', '__class__']) + view.__class__.__name__ = 'ComplexView' + view.get_serializer = Mock(side_effect=ValueError('Invalid serializer')) + view.model = mock_model + + schema = CustomAutoSchema() + schema.view = view + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + tags = schema.get_tags() + + # Should have warned about the exception + assert len(w) == 1 + assert 'ComplexView.get_serializer() raised an exception' in str(w[0].message) + + # But still recovered and got tags from view.model + assert tags == ['Recovery Models'] + + def test_get_tags_priority_order(self): + """Test that get_tags respects priority: swagger_topic > serializer.Meta.model > view.model.""" + # Set up a view with all three options + mock_model_view = Mock() + mock_model_view._meta.verbose_name_plural = 'view models' + + mock_model_serializer = Mock() + mock_model_serializer._meta.verbose_name_plural = 'serializer models' + + mock_serializer = Mock() + mock_serializer.Meta.model = mock_model_serializer + + view = Mock() + view.swagger_topic = 'priority_topic' + view.get_serializer = Mock(return_value=mock_serializer) + view.model = mock_model_view + + schema = CustomAutoSchema() + schema.view = view + + tags = schema.get_tags() + # swagger_topic should take priority + assert tags == ['Priority_Topic'] + + +class TestAuthenticatedSchemaViews: + """Unit tests for authenticated schema view classes.""" + + def test_authenticated_spectacular_api_view_requires_authentication(self): + """Test that AuthenticatedSpectacularAPIView requires authentication.""" + assert IsAuthenticated in AuthenticatedSpectacularAPIView.permission_classes + + def test_authenticated_spectacular_swagger_view_requires_authentication(self): + """Test that AuthenticatedSpectacularSwaggerView requires authentication.""" + assert IsAuthenticated in AuthenticatedSpectacularSwaggerView.permission_classes + + def test_authenticated_spectacular_redoc_view_requires_authentication(self): + """Test that AuthenticatedSpectacularRedocView requires authentication.""" + assert IsAuthenticated in AuthenticatedSpectacularRedocView.permission_classes + + +class TestFilterCredentialTypeSchema: + """Unit tests for filter_credential_type_schema postprocessing hook.""" + + def test_filters_both_schemas_correctly(self): + """Test that both CredentialTypeRequest and PatchedCredentialTypeRequest schemas are filtered.""" + result = { + 'components': { + 'schemas': { + 'CredentialTypeRequest': { + 'properties': { + 'kind': { + 'enum': [ + 'ssh', + 'vault', + 'net', + 'scm', + 'cloud', + 'registry', + 'token', + 'insights', + 'external', + 'kubernetes', + 'galaxy', + 'cryptography', + None, + ], + 'type': 'string', + } + } + }, + 'PatchedCredentialTypeRequest': { + 'properties': { + 'kind': { + 'enum': [ + 'ssh', + 'vault', + 'net', + 'scm', + 'cloud', + 'registry', + 'token', + 'insights', + 'external', + 'kubernetes', + 'galaxy', + 'cryptography', + None, + ], + 'type': 'string', + } + } + }, + } + } + } + + returned = filter_credential_type_schema(result, None, None, None) + + # POST/PUT schema: no None (required field) + assert result['components']['schemas']['CredentialTypeRequest']['properties']['kind']['enum'] == ['cloud', 'net'] + assert result['components']['schemas']['CredentialTypeRequest']['properties']['kind']['description'] == "* `cloud` - Cloud\\n* `net` - Network" + + # PATCH schema: includes None (optional field) + assert result['components']['schemas']['PatchedCredentialTypeRequest']['properties']['kind']['enum'] == ['cloud', 'net', None] + assert result['components']['schemas']['PatchedCredentialTypeRequest']['properties']['kind']['description'] == "* `cloud` - Cloud\\n* `net` - Network" + + # Other properties should be preserved + assert result['components']['schemas']['CredentialTypeRequest']['properties']['kind']['type'] == 'string' + + # Function should return the result + assert returned is result + + def test_handles_empty_result(self): + """Test graceful handling when result dict is empty.""" + result = {} + original = copy.deepcopy(result) + + returned = filter_credential_type_schema(result, None, None, None) + + assert result == original + assert returned is result + + def test_handles_missing_enum(self): + """Test that schemas without enum key are not modified.""" + result = {'components': {'schemas': {'CredentialTypeRequest': {'properties': {'kind': {'type': 'string', 'description': 'Some description'}}}}}} + original = copy.deepcopy(result) + + filter_credential_type_schema(result, None, None, None) + + assert result == original + + def test_filters_only_target_schemas(self): + """Test that only CredentialTypeRequest schemas are modified, not others.""" + result = { + 'components': { + 'schemas': { + 'CredentialTypeRequest': {'properties': {'kind': {'enum': ['ssh', 'cloud', 'net', None]}}}, + 'OtherSchema': {'properties': {'kind': {'enum': ['option1', 'option2']}}}, + } + } + } + + other_schema_before = copy.deepcopy(result['components']['schemas']['OtherSchema']) + + filter_credential_type_schema(result, None, None, None) + + # CredentialTypeRequest should be filtered (no None for required field) + assert result['components']['schemas']['CredentialTypeRequest']['properties']['kind']['enum'] == ['cloud', 'net'] + + # OtherSchema should be unchanged + assert result['components']['schemas']['OtherSchema'] == other_schema_before + + def test_handles_only_one_schema_present(self): + """Test that function works when only one target schema is present.""" + result = {'components': {'schemas': {'CredentialTypeRequest': {'properties': {'kind': {'enum': ['ssh', 'cloud', 'net', None]}}}}}} + + filter_credential_type_schema(result, None, None, None) + + assert result['components']['schemas']['CredentialTypeRequest']['properties']['kind']['enum'] == ['cloud', 'net'] + + def test_handles_missing_properties(self): + """Test graceful handling when schema has no properties key.""" + result = {'components': {'schemas': {'CredentialTypeRequest': {}}}} + original = copy.deepcopy(result) + + filter_credential_type_schema(result, None, None, None) + + assert result == original + + def test_differentiates_required_vs_optional_fields(self): + """Test that CredentialTypeRequest excludes None but PatchedCredentialTypeRequest includes it.""" + result = { + 'components': { + 'schemas': { + 'CredentialTypeRequest': {'properties': {'kind': {'enum': ['ssh', 'vault', 'net', 'scm', 'cloud', 'registry', None]}}}, + 'PatchedCredentialTypeRequest': {'properties': {'kind': {'enum': ['ssh', 'vault', 'net', 'scm', 'cloud', 'registry', None]}}}, + } + } + } + + filter_credential_type_schema(result, None, None, None) + + # POST/PUT schema: no None (required field) + assert result['components']['schemas']['CredentialTypeRequest']['properties']['kind']['enum'] == ['cloud', 'net'] + + # PATCH schema: includes None (optional field) + assert result['components']['schemas']['PatchedCredentialTypeRequest']['properties']['kind']['enum'] == ['cloud', 'net', None] diff --git a/awx/main/tests/unit/api/test_views.py b/awx/main/tests/unit/api/test_views.py index 950ae57af4a7..503ad6e854dd 100644 --- a/awx/main/tests/unit/api/test_views.py +++ b/awx/main/tests/unit/api/test_views.py @@ -50,6 +50,7 @@ def test_get_endpoints(self, mocker): 'activity_stream', 'workflow_job_templates', 'workflow_jobs', + 'analytics', ] view = ApiVersionRootView() ret = view.get(mocker.MagicMock()) @@ -65,7 +66,7 @@ def test_inherited_mixin_unattach(self): mock_request = mock.MagicMock() super(JobTemplateLabelList, view).unattach(mock_request, None, None) - assert mixin_unattach.called_with(mock_request, None, None) + mixin_unattach.assert_called_with(mock_request, None, None) class TestInventoryInventorySourcesUpdate: @@ -107,15 +108,16 @@ def exclude(self, **kwargs): mock_request = mocker.MagicMock() mock_request.user.can_access.return_value = can_access - with mocker.patch.object(InventoryInventorySourcesUpdate, 'get_object', return_value=obj): - with mocker.patch.object(InventoryInventorySourcesUpdate, 'get_serializer_context', return_value=None): - with mocker.patch('awx.api.serializers.InventoryUpdateDetailSerializer') as serializer_class: - serializer = serializer_class.return_value - serializer.to_representation.return_value = {} + mocker.patch.object(InventoryInventorySourcesUpdate, 'get_object', return_value=obj) + mocker.patch.object(InventoryInventorySourcesUpdate, 'get_serializer_context', return_value=None) + serializer_class = mocker.patch('awx.api.serializers.InventoryUpdateDetailSerializer') - view = InventoryInventorySourcesUpdate() - response = view.post(mock_request) - assert response.data == expected + serializer = serializer_class.return_value + serializer.to_representation.return_value = {} + + view = InventoryInventorySourcesUpdate() + response = view.post(mock_request) + assert response.data == expected class TestSurveySpecValidation: diff --git a/awx/main/tests/unit/commands/test_dispatcherctl.py b/awx/main/tests/unit/commands/test_dispatcherctl.py new file mode 100644 index 000000000000..50804577c36e --- /dev/null +++ b/awx/main/tests/unit/commands/test_dispatcherctl.py @@ -0,0 +1,92 @@ +import io + +import pytest + +from django.core.management.base import CommandError + +from awx.main.management.commands import dispatcherctl + + +@pytest.fixture(autouse=True) +def clear_dispatcher_env(monkeypatch, mocker): + monkeypatch.delenv('DISPATCHERD_CONFIG_FILE', raising=False) + mocker.patch.object(dispatcherctl.logging, 'basicConfig') + mocker.patch.object(dispatcherctl, 'connection', mocker.Mock(vendor='postgresql')) + + +def test_dispatcherctl_runs_control_with_generated_config(mocker): + command = dispatcherctl.Command() + command.stdout = io.StringIO() + + data = {'foo': 'bar'} + mocker.patch.object(dispatcherctl, '_build_command_data_from_args', return_value=data) + dispatcher_setup = mocker.patch.object(dispatcherctl, 'dispatcher_setup') + config_data = {'setting': 'value'} + mocker.patch.object(dispatcherctl, 'get_dispatcherd_config', return_value=config_data) + + control = mocker.Mock() + control.control_with_reply.return_value = [{'status': 'ok'}] + mocker.patch.object(dispatcherctl, 'get_control_from_settings', return_value=control) + mocker.patch.object(dispatcherctl.yaml, 'dump', return_value='payload\n') + + command.handle( + command='running', + config=dispatcherctl.DEFAULT_CONFIG_FILE, + expected_replies=1, + log_level='INFO', + ) + + dispatcher_setup.assert_called_once_with(config_data) + control.control_with_reply.assert_called_once_with('running', data=data, expected_replies=1) + assert command.stdout.getvalue() == 'payload\n' + + +def test_dispatcherctl_rejects_custom_config_path(): + command = dispatcherctl.Command() + command.stdout = io.StringIO() + + with pytest.raises(CommandError): + command.handle( + command='running', + config='/tmp/dispatcher.yml', + expected_replies=1, + log_level='INFO', + ) + + +def test_dispatcherctl_rejects_sqlite_db(mocker): + command = dispatcherctl.Command() + command.stdout = io.StringIO() + + mocker.patch.object(dispatcherctl, 'connection', mocker.Mock(vendor='sqlite')) + + with pytest.raises(CommandError, match='sqlite3'): + command.handle( + command='running', + config=dispatcherctl.DEFAULT_CONFIG_FILE, + expected_replies=1, + log_level='INFO', + ) + + +def test_dispatcherctl_raises_when_replies_missing(mocker): + command = dispatcherctl.Command() + command.stdout = io.StringIO() + + mocker.patch.object(dispatcherctl, '_build_command_data_from_args', return_value={}) + mocker.patch.object(dispatcherctl, 'dispatcher_setup') + mocker.patch.object(dispatcherctl, 'get_dispatcherd_config', return_value={}) + control = mocker.Mock() + control.control_with_reply.return_value = [{'status': 'ok'}] + mocker.patch.object(dispatcherctl, 'get_control_from_settings', return_value=control) + mocker.patch.object(dispatcherctl.yaml, 'dump', return_value='- status: ok\n') + + with pytest.raises(CommandError): + command.handle( + command='running', + config=dispatcherctl.DEFAULT_CONFIG_FILE, + expected_replies=2, + log_level='INFO', + ) + + control.control_with_reply.assert_called_once_with('running', data={}, expected_replies=2) diff --git a/awx/main/tests/unit/models/test_credential.py b/awx/main/tests/unit/models/test_credential.py index 0dc8daff3356..437945efc27d 100644 --- a/awx/main/tests/unit/models/test_credential.py +++ b/awx/main/tests/unit/models/test_credential.py @@ -2,7 +2,13 @@ import pytest +from types import SimpleNamespace +from unittest import mock + from awx.main.models import Credential, CredentialType +from awx.main.models.credential import CredentialTypeHelper, ManagedCredentialType + +from django.apps import apps @pytest.mark.django_db @@ -16,3 +22,113 @@ def test_custom_cred_with_empty_encrypted_field(): ct = CredentialType(name='My Custom Cred', kind='custom', inputs={'fields': [{'id': 'some_field', 'label': 'My Field', 'secret': True}]}) cred = Credential(id=4, name='Testing 1 2 3', credential_type=ct, inputs={}) assert cred.encrypt_field('some_field', None) is None + + +@pytest.mark.parametrize( + ( + 'apps', + 'app_config', + ), + [ + ( + apps, + None, + ), + ( + None, + apps.get_app_config('main'), + ), + ], +) +def test__get_credential_type_class(apps, app_config): + ct = CredentialType._get_credential_type_class(apps=apps, app_config=app_config) + assert ct.__name__ == 'CredentialType' + + +def test__get_credential_type_class_invalid_params(): + with pytest.raises(ValueError) as e: + CredentialType._get_credential_type_class(apps=apps, app_config=apps.get_app_config('main')) + + assert type(e.value) is ValueError + assert str(e.value) == 'Expected only apps or app_config to be defined, not both' + + +def test_credential_context_property(): + """Test that credential context property initializes empty dict and persists across accesses.""" + ct = CredentialType(name='Test Cred', kind='vault') + cred = Credential(id=1, name='Test Credential', credential_type=ct, inputs={}) + + # First access should return empty dict + context = cred.context + assert context == {} + + # Modify the context + context['test_key'] = 'test_value' + + # Second access should return the same dict with modifications + assert cred.context == {'test_key': 'test_value'} + assert cred.context is context # Same object reference + + +def test_credential_context_property_independent_instances(): + """Test that context property is independent between credential instances.""" + ct = CredentialType(name='Test Cred', kind='vault') + cred1 = Credential(id=1, name='Cred 1', credential_type=ct, inputs={}) + cred2 = Credential(id=2, name='Cred 2', credential_type=ct, inputs={}) + + cred1.context['key1'] = 'value1' + cred2.context['key2'] = 'value2' + + assert cred1.context == {'key1': 'value1'} + assert cred2.context == {'key2': 'value2'} + assert cred1.context is not cred2.context + + +def test_load_plugin_passes_description(): + plugin = SimpleNamespace(name='test_plugin', inputs={'fields': []}, backend=None, plugin_description='A test plugin') + CredentialType.load_plugin('test_ns', plugin) + entry = ManagedCredentialType.registry['test_ns'] + assert entry.description == 'A test plugin' + del ManagedCredentialType.registry['test_ns'] + + +def test_load_plugin_missing_description(): + plugin = SimpleNamespace(name='test_plugin', inputs={'fields': []}, backend=None) + CredentialType.load_plugin('test_ns', plugin) + entry = ManagedCredentialType.registry['test_ns'] + assert entry.description == '' + del ManagedCredentialType.registry['test_ns'] + + +def test_get_creation_params_external_includes_description(): + cred_type = SimpleNamespace(namespace='test_ns', kind='external', name='Test', description='My description') + params = CredentialTypeHelper.get_creation_params(cred_type) + assert params['description'] == 'My description' + + +def test_get_creation_params_external_missing_description(): + cred_type = SimpleNamespace(namespace='test_ns', kind='external', name='Test') + params = CredentialTypeHelper.get_creation_params(cred_type) + assert params['description'] == '' + + +@pytest.mark.django_db +def test_setup_tower_managed_defaults_updates_description(): + registry_entry = SimpleNamespace( + namespace='test_ns', + kind='external', + name='Test Plugin', + inputs={'fields': []}, + backend=None, + description='Updated description', + ) + # Create an existing credential type with no description + ct = CredentialType.objects.create(name='Test Plugin', kind='external', namespace='old_ns') + assert ct.description == '' + + with mock.patch.dict(ManagedCredentialType.registry, {'test_ns': registry_entry}, clear=True): + CredentialType._setup_tower_managed_defaults() + + ct.refresh_from_db() + assert ct.description == 'Updated description' + assert ct.namespace == 'test_ns' diff --git a/awx/main/tests/unit/models/test_events.py b/awx/main/tests/unit/models/test_events.py index a38df57fff6c..920b8572ea12 100644 --- a/awx/main/tests/unit/models/test_events.py +++ b/awx/main/tests/unit/models/test_events.py @@ -1,5 +1,5 @@ from datetime import datetime -from django.utils.timezone import utc +from datetime import timezone import pytest from awx.main.models import JobEvent, ProjectUpdateEvent, AdHocCommandEvent, InventoryUpdateEvent, SystemJobEvent @@ -18,7 +18,7 @@ @pytest.mark.parametrize('created', [datetime(2018, 1, 1).isoformat(), datetime(2018, 1, 1)]) def test_event_parse_created(job_identifier, cls, created): event = cls.create_from_data(**{job_identifier: 123, 'created': created}) - assert event.created == datetime(2018, 1, 1).replace(tzinfo=utc) + assert event.created == datetime(2018, 1, 1).replace(tzinfo=timezone.utc) @pytest.mark.parametrize( diff --git a/awx/main/tests/unit/models/test_jobs.py b/awx/main/tests/unit/models/test_jobs.py index 2f030a57c32c..bd3422d7b3b0 100644 --- a/awx/main/tests/unit/models/test_jobs.py +++ b/awx/main/tests/unit/models/test_jobs.py @@ -1,124 +1,154 @@ # -*- coding: utf-8 -*- import json import os -import time - import pytest +from unittest import mock from awx.main.models import ( - Job, Inventory, Host, ) +from awx.main.tasks.facts import start_fact_cache, finish_fact_cache + +from django.utils.timezone import now + +from datetime import timedelta + +import time @pytest.fixture -def hosts(inventory): +def ref_time(): + return now() - timedelta(seconds=5) + + +@pytest.fixture +def hosts(ref_time): + inventory = Inventory(id=5) return [ - Host(name='host1', ansible_facts={"a": 1, "b": 2}, inventory=inventory), - Host(name='host2', ansible_facts={"a": 1, "b": 2}, inventory=inventory), - Host(name='host3', ansible_facts={"a": 1, "b": 2}, inventory=inventory), - Host(name=u'Iñtërnâtiônàlizætiøn', ansible_facts={"a": 1, "b": 2}, inventory=inventory), + Host(name='host1', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=ref_time, inventory=inventory), + Host(name='host2', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=ref_time, inventory=inventory), + Host(name='host3', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=ref_time, inventory=inventory), + Host(name=u'Iñtërnâtiônàlizætiøn', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=ref_time, inventory=inventory), ] -@pytest.fixture -def inventory(): - return Inventory(id=5) +def test_start_job_fact_cache(hosts, tmpdir): + # Create artifacts dir inside tmpdir + artifacts_dir = tmpdir.mkdir("artifacts") + # Assign a mock inventory ID + inventory_id = 42 -@pytest.fixture -def job(mocker, hosts, inventory): - j = Job(inventory=inventory, id=2) - j._get_inventory_hosts = mocker.Mock(return_value=hosts) - return j + # Call the function WITHOUT log_data — the decorator handles it + start_fact_cache(hosts, artifacts_dir=str(artifacts_dir), timeout=0, inventory_id=inventory_id) + + # Fact files are written into artifacts_dir/fact_cache/ + fact_cache_dir = os.path.join(artifacts_dir, 'fact_cache') + + for host in hosts: + filepath = os.path.join(fact_cache_dir, host.name) + assert os.path.exists(filepath) + with open(filepath, 'r', encoding='utf-8') as f: + assert json.load(f) == host.ansible_facts + + +def test_fact_cache_with_invalid_path_traversal(tmpdir): + hosts = [ + Host( + name='../foo', + ansible_facts={"a": 1, "b": 2}, + ), + ] + artifacts_dir = tmpdir.mkdir("artifacts") + inventory_id = 42 + + start_fact_cache(hosts, artifacts_dir=str(artifacts_dir), timeout=0, inventory_id=inventory_id) + + # Fact cache directory (safe location) + fact_cache_dir = os.path.join(artifacts_dir, 'fact_cache') + # The bad host name should not produce a file + assert not os.path.exists(os.path.join(fact_cache_dir, '../foo')) -def test_start_job_fact_cache(hosts, job, inventory, tmpdir): + # Make sure the fact_cache dir exists and is still empty + assert os.listdir(fact_cache_dir) == [] + + +def test_start_job_fact_cache_past_timeout(hosts, tmpdir): fact_cache = os.path.join(tmpdir, 'facts') - last_modified = job.start_job_fact_cache(fact_cache, timeout=0) + start_fact_cache(hosts, fact_cache, timeout=2) + + for host in hosts: + assert not os.path.exists(os.path.join(fact_cache, host.name)) + ret = start_fact_cache(hosts, fact_cache, timeout=2) + assert ret is None + +def test_start_job_fact_cache_within_timeout(hosts, tmpdir): + artifacts_dir = tmpdir.mkdir("artifacts") + + # The hosts fixture was modified 5s ago, which is less than 7s + start_fact_cache(hosts, str(artifacts_dir), timeout=7) + + fact_cache_dir = os.path.join(artifacts_dir, 'fact_cache') for host in hosts: - filepath = os.path.join(fact_cache, host.name) + filepath = os.path.join(fact_cache_dir, host.name) assert os.path.exists(filepath) with open(filepath, 'r') as f: - assert f.read() == json.dumps(host.ansible_facts) - assert os.path.getmtime(filepath) <= last_modified + assert json.load(f) == host.ansible_facts -def test_fact_cache_with_invalid_path_traversal(job, inventory, tmpdir, mocker): - job._get_inventory_hosts = mocker.Mock( - return_value=[ - Host( - name='../foo', - ansible_facts={"a": 1, "b": 2}, - ), - ] - ) +def test_finish_job_fact_cache_clear(hosts, mocker, ref_time, tmpdir): + artifacts_dir = str(tmpdir.mkdir("artifacts")) + inventory_id = 5 - fact_cache = os.path.join(tmpdir, 'facts') - job.start_job_fact_cache(fact_cache, timeout=0) - # a file called "foo" should _not_ be written outside the facts dir - assert os.listdir(os.path.join(fact_cache, '..')) == ['facts'] + start_fact_cache(hosts, artifacts_dir=artifacts_dir, timeout=0, inventory_id=inventory_id) + mocker.patch('awx.main.tasks.facts.bulk_update_sorted_by_id') -def test_finish_job_fact_cache_with_existing_data(job, hosts, inventory, mocker, tmpdir): - fact_cache = os.path.join(tmpdir, 'facts') - last_modified = job.start_job_fact_cache(fact_cache, timeout=0) + # Remove the fact file for hosts[1] to simulate ansible's clear_facts + fact_cache_dir = os.path.join(artifacts_dir, 'fact_cache') + os.remove(os.path.join(fact_cache_dir, hosts[1].name)) - bulk_update = mocker.patch('django.db.models.query.QuerySet.bulk_update') + hosts_qs = mock.MagicMock() + hosts_qs.filter.return_value.order_by.return_value.iterator.return_value = iter(hosts) - ansible_facts_new = {"foo": "bar"} - filepath = os.path.join(fact_cache, hosts[1].name) - with open(filepath, 'w') as f: - f.write(json.dumps(ansible_facts_new)) - f.flush() - # I feel kind of gross about calling `os.utime` by hand, but I noticed - # that in our container-based dev environment, the resolution for - # `os.stat()` after a file write was over a second, and I don't want to put - # a sleep() in this test - new_modification_time = time.time() + 3600 - os.utime(filepath, (new_modification_time, new_modification_time)) + finish_fact_cache(hosts_qs, artifacts_dir=artifacts_dir, inventory_id=inventory_id) - job.finish_job_fact_cache(fact_cache, last_modified) + # hosts[1] should have had its facts cleared (file was missing, job_created=None) + assert hosts[1].ansible_facts == {} + assert hosts[1].ansible_facts_modified > ref_time + # Other hosts should be unmodified (fact files exist but weren't changed by ansible) for host in (hosts[0], hosts[2], hosts[3]): assert host.ansible_facts == {"a": 1, "b": 2} - assert host.ansible_facts_modified is None - assert hosts[1].ansible_facts == ansible_facts_new - bulk_update.assert_called_once_with([hosts[1]], ['ansible_facts', 'ansible_facts_modified']) + assert host.ansible_facts_modified == ref_time -def test_finish_job_fact_cache_with_bad_data(job, hosts, inventory, mocker, tmpdir): - fact_cache = os.path.join(tmpdir, 'facts') - last_modified = job.start_job_fact_cache(fact_cache, timeout=0) +def test_finish_job_fact_cache_with_bad_data(hosts, mocker, tmpdir): + artifacts_dir = str(tmpdir.mkdir("artifacts")) + inventory_id = 5 + + start_fact_cache(hosts, artifacts_dir=artifacts_dir, timeout=0, inventory_id=inventory_id) - bulk_update = mocker.patch('django.db.models.query.QuerySet.bulk_update') + bulk_update = mocker.patch('awx.main.tasks.facts.bulk_update_sorted_by_id') + # Overwrite fact files with invalid JSON and set future mtime + fact_cache_dir = os.path.join(artifacts_dir, 'fact_cache') for h in hosts: - filepath = os.path.join(fact_cache, h.name) + filepath = os.path.join(fact_cache_dir, h.name) with open(filepath, 'w') as f: f.write('not valid json!') f.flush() new_modification_time = time.time() + 3600 os.utime(filepath, (new_modification_time, new_modification_time)) - job.finish_job_fact_cache(fact_cache, last_modified) - - bulk_update.assert_not_called() - - -def test_finish_job_fact_cache_clear(job, hosts, inventory, mocker, tmpdir): - fact_cache = os.path.join(tmpdir, 'facts') - last_modified = job.start_job_fact_cache(fact_cache, timeout=0) - - bulk_update = mocker.patch('django.db.models.query.QuerySet.bulk_update') + hosts_qs = mock.MagicMock() + hosts_qs.filter.return_value.order_by.return_value.iterator.return_value = iter(hosts) - os.remove(os.path.join(fact_cache, hosts[1].name)) - job.finish_job_fact_cache(fact_cache, last_modified) + finish_fact_cache(hosts_qs, artifacts_dir=artifacts_dir, inventory_id=inventory_id) - for host in (hosts[0], hosts[2], hosts[3]): - assert host.ansible_facts == {"a": 1, "b": 2} - assert host.ansible_facts_modified is None - assert hosts[1].ansible_facts == {} - bulk_update.assert_called_once_with([hosts[1]], ['ansible_facts', 'ansible_facts_modified']) + # Invalid JSON should be skipped — no hosts updated + updated_hosts = bulk_update.call_args[0][1] + assert updated_hosts == [] diff --git a/awx/main/tests/unit/models/test_label.py b/awx/main/tests/unit/models/test_label.py index e049a8857867..8017782ffa5c 100644 --- a/awx/main/tests/unit/models/test_label.py +++ b/awx/main/tests/unit/models/test_label.py @@ -11,7 +11,6 @@ WorkflowJobNode, ) - mock_query_set = mock.MagicMock() mock_objects = mock.MagicMock(filter=mock.MagicMock(return_value=mock_query_set)) diff --git a/awx/main/tests/unit/models/test_receptor_address.py b/awx/main/tests/unit/models/test_receptor_address.py new file mode 100644 index 000000000000..f18e1a9018a1 --- /dev/null +++ b/awx/main/tests/unit/models/test_receptor_address.py @@ -0,0 +1,32 @@ +from awx.main.models import ReceptorAddress +import pytest + +ReceptorAddress() + + +@pytest.mark.parametrize( + 'address, protocol, port, websocket_path, expected', + [ + ('foo', 'tcp', 27199, '', 'foo:27199'), + ('bar', 'ws', 6789, '', 'wss://bar:6789'), + ('mal', 'ws', 6789, 'path', 'wss://mal:6789/path'), + ('example.com', 'ws', 443, 'path', 'wss://example.com:443/path'), + ], +) +def test_get_full_address(address, protocol, port, websocket_path, expected): + receptor_address = ReceptorAddress(address=address, protocol=protocol, port=port, websocket_path=websocket_path) + assert receptor_address.get_full_address() == expected + + +@pytest.mark.parametrize( + 'protocol, expected', + [ + ('tcp', 'tcp-peer'), + ('ws', 'ws-peer'), + ('wss', 'ws-peer'), + ('foo', None), + ], +) +def test_get_peer_type(protocol, expected): + receptor_address = ReceptorAddress(protocol=protocol) + assert receptor_address.get_peer_type() == expected diff --git a/awx/main/tests/unit/models/test_survey_models.py b/awx/main/tests/unit/models/test_survey_models.py index 57058930eacc..6d9eb5dec999 100644 --- a/awx/main/tests/unit/models/test_survey_models.py +++ b/awx/main/tests/unit/models/test_survey_models.py @@ -18,7 +18,7 @@ def __call__(self, value): @pytest.mark.survey -class SurveyVariableValidation: +class TestSurveyVariableValidation: def test_survey_answers_as_string(self, job_template_factory): objects = job_template_factory('job-template-with-survey', survey=[{'variable': 'var1', 'type': 'text'}], persisted=False) jt = objects.job_template @@ -57,7 +57,7 @@ def test_job_template_survey_variable_validation(self, job_template_factory): accepted, rejected, errors = obj.accept_or_ignore_variables({"a": 5}) assert rejected == {"a": 5} assert accepted == {} - assert str(errors[0]) == "Value 5 for 'a' expected to be a string." + assert str(errors['variables_needed_to_start'][0]) == "Value 5 for 'a' expected to be a string." def test_job_template_survey_default_variable_validation(self, job_template_factory): objects = job_template_factory( @@ -88,7 +88,7 @@ def test_job_template_survey_default_variable_validation(self, job_template_fact obj.survey_enabled = True accepted, _, errors = obj.accept_or_ignore_variables({"a": 2}) - assert accepted == {{"a": 2.0}} + assert accepted == {"a": 2.0} assert not errors @@ -176,22 +176,22 @@ def test_display_survey_spec_encrypts_default(survey_spec_factory): @pytest.mark.survey @pytest.mark.parametrize( - "question_type,default,min,max,expect_use,expect_value", + "question_type,default,min,max,expect_valid,expect_use,expect_value", [ - ("text", "", 0, 0, True, ''), # default used - ("text", "", 1, 0, False, 'N/A'), # value less than min length - ("password", "", 1, 0, False, 'N/A'), # passwords behave the same as text - ("multiplechoice", "", 0, 0, False, 'N/A'), # historical bug - ("multiplechoice", "zeb", 0, 0, False, 'N/A'), # zeb not in choices - ("multiplechoice", "coffee", 0, 0, True, 'coffee'), - ("multiselect", None, 0, 0, False, 'N/A'), # NOTE: Behavior is arguable, value of [] may be prefered - ("multiselect", "", 0, 0, False, 'N/A'), - ("multiselect", ["zeb"], 0, 0, False, 'N/A'), - ("multiselect", ["milk"], 0, 0, True, ["milk"]), - ("multiselect", ["orange\nmilk"], 0, 0, False, 'N/A'), # historical bug + ("text", "", 0, 0, True, False, 'N/A'), # valid but empty default not sent for optional question + ("text", "", 1, 0, False, False, 'N/A'), # value less than min length + ("password", "", 1, 0, False, False, 'N/A'), # passwords behave the same as text + ("multiplechoice", "", 0, 0, False, False, 'N/A'), # historical bug + ("multiplechoice", "zeb", 0, 0, False, False, 'N/A'), # zeb not in choices + ("multiplechoice", "coffee", 0, 0, True, True, 'coffee'), + ("multiselect", None, 0, 0, False, False, 'N/A'), # NOTE: Behavior is arguable, value of [] may be prefered + ("multiselect", "", 0, 0, False, False, 'N/A'), + ("multiselect", ["zeb"], 0, 0, False, False, 'N/A'), + ("multiselect", ["milk"], 0, 0, True, True, ["milk"]), + ("multiselect", ["orange\nmilk"], 0, 0, False, False, 'N/A'), # historical bug ], ) -def test_optional_survey_question_defaults(survey_spec_factory, question_type, default, min, max, expect_use, expect_value): +def test_optional_survey_question_defaults(survey_spec_factory, question_type, default, min, max, expect_valid, expect_use, expect_value): spec = survey_spec_factory( [ { @@ -208,7 +208,7 @@ def test_optional_survey_question_defaults(survey_spec_factory, question_type, d jt = JobTemplate(name="test-jt", survey_spec=spec, survey_enabled=True) defaulted_extra_vars = jt._update_unified_job_kwargs({}, {}) element = spec['spec'][0] - if expect_use: + if expect_valid: assert jt._survey_element_validation(element, {element['variable']: element['default']}) == [] else: assert jt._survey_element_validation(element, {element['variable']: element['default']}) @@ -218,6 +218,28 @@ def test_optional_survey_question_defaults(survey_spec_factory, question_type, d assert 'c' not in defaulted_extra_vars['extra_vars'] +@pytest.mark.survey +def test_optional_survey_empty_default_with_runtime_extra_var(survey_spec_factory): + """When a user explicitly provides an empty string at runtime for an optional + survey question, the variable should still be included in extra_vars.""" + spec = survey_spec_factory( + [ + { + "required": False, + "default": "", + "choices": "", + "variable": "c", + "min": 0, + "max": 0, + "type": "text", + }, + ] + ) + jt = JobTemplate(name="test-jt", survey_spec=spec, survey_enabled=True) + defaulted_extra_vars = jt._update_unified_job_kwargs({}, {'extra_vars': json.dumps({'c': ''})}) + assert json.loads(defaulted_extra_vars['extra_vars'])['c'] == '' + + @pytest.mark.survey @pytest.mark.parametrize( "question_type,default,maxlen,kwargs,expected", diff --git a/awx/main/tests/unit/models/test_unified_job_unit.py b/awx/main/tests/unit/models/test_unified_job_unit.py index c7f62225c8e1..2fa8807dff74 100644 --- a/awx/main/tests/unit/models/test_unified_job_unit.py +++ b/awx/main/tests/unit/models/test_unified_job_unit.py @@ -1,4 +1,3 @@ -import pytest from unittest import mock from awx.main.models import UnifiedJob, UnifiedJobTemplate, WorkflowJob, WorkflowJobNode, WorkflowApprovalTemplate, Job, User, Project, JobTemplate, Inventory @@ -22,52 +21,6 @@ def test_unified_job_workflow_attributes(): assert job.workflow_job_id == 1 -def mock_on_commit(f): - f() - - -@pytest.fixture -def unified_job(mocker): - mocker.patch.object(UnifiedJob, 'can_cancel', return_value=True) - j = UnifiedJob() - j.status = 'pending' - j.cancel_flag = None - j.save = mocker.MagicMock() - j.websocket_emit_status = mocker.MagicMock() - j.fallback_cancel = mocker.MagicMock() - return j - - -def test_cancel(unified_job): - with mock.patch('awx.main.models.unified_jobs.connection.on_commit', wraps=mock_on_commit): - unified_job.cancel() - - assert unified_job.cancel_flag is True - assert unified_job.status == 'canceled' - assert unified_job.job_explanation == '' - # Note: the websocket emit status check is just reflecting the state of the current code. - # Some more thought may want to go into only emitting canceled if/when the job record - # status is changed to canceled. Unlike, currently, where it's emitted unconditionally. - unified_job.websocket_emit_status.assert_called_with("canceled") - assert [(args, kwargs) for args, kwargs in unified_job.save.call_args_list] == [ - ((), {'update_fields': ['cancel_flag', 'start_args']}), - ((), {'update_fields': ['status']}), - ] - - -def test_cancel_job_explanation(unified_job): - job_explanation = 'giggity giggity' - - with mock.patch('awx.main.models.unified_jobs.connection.on_commit'): - unified_job.cancel(job_explanation=job_explanation) - - assert unified_job.job_explanation == job_explanation - assert [(args, kwargs) for args, kwargs in unified_job.save.call_args_list] == [ - ((), {'update_fields': ['cancel_flag', 'start_args', 'job_explanation']}), - ((), {'update_fields': ['status']}), - ] - - def test_organization_copy_to_jobs(): """ All unified job types should infer their organization from their template organization @@ -107,7 +60,11 @@ def test_job_metavars(self): result_hash['{}_user_id'.format(name)] = 47 result_hash['{}_inventory_id'.format(name)] = 45 result_hash['{}_inventory_name'.format(name)] = 'example-inv' - assert Job(name='fake-job', pk=42, id=42, launch_type='manual', created_by=maker, inventory=inv).awx_meta_vars() == result_hash + result_hash['{}_execution_node'.format(name)] = 'example-exec-node' + assert ( + Job(name='fake-job', pk=42, id=42, launch_type='manual', created_by=maker, inventory=inv, execution_node='example-exec-node').awx_meta_vars() + == result_hash + ) def test_project_update_metavars(self): data = Job( diff --git a/awx/main/tests/unit/models/test_workflow_unit.py b/awx/main/tests/unit/models/test_workflow_unit.py index dc01c3301f6d..7ac2009403de 100644 --- a/awx/main/tests/unit/models/test_workflow_unit.py +++ b/awx/main/tests/unit/models/test_workflow_unit.py @@ -155,35 +155,35 @@ def test_node_getter_and_setters(): class TestWorkflowJobCreate: def test_create_no_prompts(self, wfjt_node_no_prompts, workflow_job_unit, mocker): mock_create = mocker.MagicMock() - with mocker.patch('awx.main.models.WorkflowJobNode.objects.create', mock_create): - wfjt_node_no_prompts.create_workflow_job_node(workflow_job=workflow_job_unit) - mock_create.assert_called_once_with( - all_parents_must_converge=False, - extra_data={}, - survey_passwords={}, - char_prompts=wfjt_node_no_prompts.char_prompts, - inventory=None, - unified_job_template=wfjt_node_no_prompts.unified_job_template, - workflow_job=workflow_job_unit, - identifier=mocker.ANY, - execution_environment=None, - ) + mocker.patch('awx.main.models.WorkflowJobNode.objects.create', mock_create) + wfjt_node_no_prompts.create_workflow_job_node(workflow_job=workflow_job_unit) + mock_create.assert_called_once_with( + all_parents_must_converge=False, + extra_data={}, + survey_passwords={}, + char_prompts=wfjt_node_no_prompts.char_prompts, + inventory=None, + unified_job_template=wfjt_node_no_prompts.unified_job_template, + workflow_job=workflow_job_unit, + identifier=mocker.ANY, + execution_environment=None, + ) def test_create_with_prompts(self, wfjt_node_with_prompts, workflow_job_unit, credential, mocker): mock_create = mocker.MagicMock() - with mocker.patch('awx.main.models.WorkflowJobNode.objects.create', mock_create): - wfjt_node_with_prompts.create_workflow_job_node(workflow_job=workflow_job_unit) - mock_create.assert_called_once_with( - all_parents_must_converge=False, - extra_data={}, - survey_passwords={}, - char_prompts=wfjt_node_with_prompts.char_prompts, - inventory=wfjt_node_with_prompts.inventory, - unified_job_template=wfjt_node_with_prompts.unified_job_template, - workflow_job=workflow_job_unit, - identifier=mocker.ANY, - execution_environment=None, - ) + mocker.patch('awx.main.models.WorkflowJobNode.objects.create', mock_create) + wfjt_node_with_prompts.create_workflow_job_node(workflow_job=workflow_job_unit) + mock_create.assert_called_once_with( + all_parents_must_converge=False, + extra_data={}, + survey_passwords={}, + char_prompts=wfjt_node_with_prompts.char_prompts, + inventory=wfjt_node_with_prompts.inventory, + unified_job_template=wfjt_node_with_prompts.unified_job_template, + workflow_job=workflow_job_unit, + identifier=mocker.ANY, + execution_environment=None, + ) @pytest.mark.django_db diff --git a/awx/main/tests/unit/notifications/test_awssns.py b/awx/main/tests/unit/notifications/test_awssns.py new file mode 100644 index 000000000000..0d18821fe3e3 --- /dev/null +++ b/awx/main/tests/unit/notifications/test_awssns.py @@ -0,0 +1,26 @@ +from unittest import mock +from django.core.mail.message import EmailMessage + +import awx.main.notifications.awssns_backend as awssns_backend + + +def test_send_messages(): + with mock.patch('awx.main.notifications.awssns_backend.AWSSNSBackend._sns_publish') as sns_publish_mock: + aws_region = 'us-east-1' + sns_topic = f"arn:aws:sns:{aws_region}:111111111111:topic-mock" + backend = awssns_backend.AWSSNSBackend(aws_region=aws_region, aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None) + message = EmailMessage( + 'test subject', + {'body': 'test body'}, + [], + [ + sns_topic, + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + sns_publish_mock.assert_called_once_with(topic_arn=sns_topic, message=message.body) + assert sent_messages == 1 diff --git a/awx/main/tests/unit/notifications/test_grafana.py b/awx/main/tests/unit/notifications/test_grafana.py index 70750e33150b..d4b9c31aa2d3 100644 --- a/awx/main/tests/unit/notifications/test_grafana.py +++ b/awx/main/tests/unit/notifications/test_grafana.py @@ -13,7 +13,7 @@ def test_send_messages(): m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() m['subject'] = "test subject" - backend = grafana_backend.GrafanaBackend("testapikey") + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId='', panelId='') message = EmailMessage( m['subject'], {"started": m['started'], "finished": m['finished']}, @@ -43,7 +43,7 @@ def test_send_messages_with_no_verify_ssl(): m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() m['subject'] = "test subject" - backend = grafana_backend.GrafanaBackend("testapikey", grafana_no_verify_ssl=True) + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId='', panelId='', grafana_no_verify_ssl=True) message = EmailMessage( m['subject'], {"started": m['started'], "finished": m['finished']}, @@ -74,7 +74,7 @@ def test_send_messages_with_dashboardid(dashboardId): m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() m['subject'] = "test subject" - backend = grafana_backend.GrafanaBackend("testapikey", dashboardId=dashboardId) + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId=dashboardId, panelId='') message = EmailMessage( m['subject'], {"started": m['started'], "finished": m['finished']}, @@ -97,7 +97,7 @@ def test_send_messages_with_dashboardid(dashboardId): assert sent_messages == 1 -@pytest.mark.parametrize("panelId", [42, 0]) +@pytest.mark.parametrize("panelId", ['42', '0']) def test_send_messages_with_panelid(panelId): with mock.patch('awx.main.notifications.grafana_backend.requests') as requests_mock: requests_mock.post.return_value.status_code = 200 @@ -105,7 +105,7 @@ def test_send_messages_with_panelid(panelId): m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() m['subject'] = "test subject" - backend = grafana_backend.GrafanaBackend("testapikey", dashboardId=None, panelId=panelId) + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId='', panelId=panelId) message = EmailMessage( m['subject'], {"started": m['started'], "finished": m['finished']}, @@ -122,7 +122,7 @@ def test_send_messages_with_panelid(panelId): requests_mock.post.assert_called_once_with( 'https://example.com/api/annotations', headers={'Content-Type': 'application/json', 'Authorization': 'Bearer testapikey'}, - json={'text': 'test subject', 'isRegion': True, 'timeEnd': 120000, 'panelId': panelId, 'time': 60000}, + json={'text': 'test subject', 'isRegion': True, 'timeEnd': 120000, 'panelId': int(panelId), 'time': 60000}, verify=True, ) assert sent_messages == 1 @@ -135,7 +135,7 @@ def test_send_messages_with_bothids(): m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() m['subject'] = "test subject" - backend = grafana_backend.GrafanaBackend("testapikey", dashboardId=42, panelId=42) + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId='42', panelId='42') message = EmailMessage( m['subject'], {"started": m['started'], "finished": m['finished']}, @@ -158,6 +158,36 @@ def test_send_messages_with_bothids(): assert sent_messages == 1 +def test_send_messages_with_emptyids(): + with mock.patch('awx.main.notifications.grafana_backend.requests') as requests_mock: + requests_mock.post.return_value.status_code = 200 + m = {} + m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() + m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() + m['subject'] = "test subject" + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId='', panelId='') + message = EmailMessage( + m['subject'], + {"started": m['started'], "finished": m['finished']}, + [], + [ + 'https://example.com', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + requests_mock.post.assert_called_once_with( + 'https://example.com/api/annotations', + headers={'Content-Type': 'application/json', 'Authorization': 'Bearer testapikey'}, + json={'text': 'test subject', 'isRegion': True, 'timeEnd': 120000, 'time': 60000}, + verify=True, + ) + assert sent_messages == 1 + + def test_send_messages_with_tags(): with mock.patch('awx.main.notifications.grafana_backend.requests') as requests_mock: requests_mock.post.return_value.status_code = 200 @@ -165,7 +195,7 @@ def test_send_messages_with_tags(): m['started'] = dt.datetime.utcfromtimestamp(60).isoformat() m['finished'] = dt.datetime.utcfromtimestamp(120).isoformat() m['subject'] = "test subject" - backend = grafana_backend.GrafanaBackend("testapikey", dashboardId=None, panelId=None, annotation_tags=["ansible"]) + backend = grafana_backend.GrafanaBackend("testapikey", dashboardId='', panelId='', annotation_tags=["ansible"]) message = EmailMessage( m['subject'], {"started": m['started'], "finished": m['finished']}, diff --git a/awx/main/tests/unit/notifications/test_webhook.py b/awx/main/tests/unit/notifications/test_webhook.py index b2c92c59ab39..4abbf45b70ee 100644 --- a/awx/main/tests/unit/notifications/test_webhook.py +++ b/awx/main/tests/unit/notifications/test_webhook.py @@ -226,3 +226,140 @@ def test_send_messages_with_additional_headers(): allow_redirects=False, ) assert sent_messages == 1 + + +def test_send_messages_with_redirects_ok(): + with mock.patch('awx.main.notifications.webhook_backend.requests') as requests_mock, mock.patch( + 'awx.main.notifications.webhook_backend.get_awx_http_client_headers' + ) as version_mock: + # First two calls return redirects, third call returns 200 + requests_mock.post.side_effect = [ + mock.Mock(status_code=301, headers={"Location": "http://redirect1.com"}), + mock.Mock(status_code=307, headers={"Location": "http://redirect2.com"}), + mock.Mock(status_code=200), + ] + version_mock.return_value = {'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'} + backend = webhook_backend.WebhookBackend('POST', None) + message = EmailMessage( + 'test subject', + {'text': 'test body'}, + [], + [ + 'http://example.com', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + assert requests_mock.post.call_count == 3 + requests_mock.post.assert_called_with( + url='http://redirect2.com', + auth=None, + data=json.dumps({'text': 'test body'}, ensure_ascii=False).encode('utf-8'), + headers={'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'}, + verify=True, + allow_redirects=False, + ) + assert sent_messages == 1 + + +def test_send_messages_with_redirects_blank(): + with mock.patch('awx.main.notifications.webhook_backend.requests') as requests_mock, mock.patch( + 'awx.main.notifications.webhook_backend.get_awx_http_client_headers' + ) as version_mock, mock.patch('awx.main.notifications.webhook_backend.logger') as logger_mock: + # First call returns a redirect with Location header, second call returns 301 but NO Location header + requests_mock.post.side_effect = [ + mock.Mock(status_code=301, headers={"Location": "http://redirect1.com"}), + mock.Mock(status_code=301, headers={}), # 301 with no Location header + ] + version_mock.return_value = {'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'} + backend = webhook_backend.WebhookBackend('POST', None, fail_silently=True) + message = EmailMessage( + 'test subject', + {'text': 'test body'}, + [], + [ + 'http://example.com', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + # Should make 2 requests (initial + 1 redirect attempt) + assert requests_mock.post.call_count == 2 + # The error message should be logged + logger_mock.error.assert_called_once() + error_call_args = logger_mock.error.call_args[0][0] + assert "redirect to a blank URL" in error_call_args + assert sent_messages == 0 + + +def test_send_messages_with_redirects_max_retries_exceeded(): + with mock.patch('awx.main.notifications.webhook_backend.requests') as requests_mock, mock.patch( + 'awx.main.notifications.webhook_backend.get_awx_http_client_headers' + ) as version_mock, mock.patch('awx.main.notifications.webhook_backend.logger') as logger_mock: + # Return MAX_RETRIES (5) redirect responses to exceed the retry limit + requests_mock.post.side_effect = [ + mock.Mock(status_code=301, headers={"Location": "http://redirect1.com"}), + mock.Mock(status_code=301, headers={"Location": "http://redirect2.com"}), + mock.Mock(status_code=307, headers={"Location": "http://redirect3.com"}), + mock.Mock(status_code=301, headers={"Location": "http://redirect4.com"}), + mock.Mock(status_code=307, headers={"Location": "http://redirect5.com"}), + ] + version_mock.return_value = {'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'} + backend = webhook_backend.WebhookBackend('POST', None, fail_silently=True) + message = EmailMessage( + 'test subject', + {'text': 'test body'}, + [], + [ + 'http://example.com', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + # Should make exactly 5 requests (MAX_RETRIES) + assert requests_mock.post.call_count == 5 + # The error message should be logged for exceeding max retries + logger_mock.error.assert_called_once() + error_call_args = logger_mock.error.call_args[0][0] + assert "max number of retries" in error_call_args + assert "[5]" in error_call_args + assert sent_messages == 0 + + +def test_send_messages_with_error_status_code(): + with mock.patch('awx.main.notifications.webhook_backend.requests') as requests_mock, mock.patch( + 'awx.main.notifications.webhook_backend.get_awx_http_client_headers' + ) as version_mock, mock.patch('awx.main.notifications.webhook_backend.logger') as logger_mock: + # Return a 404 error status code + requests_mock.post.return_value = mock.Mock(status_code=404) + version_mock.return_value = {'Content-Type': 'application/json', 'User-Agent': 'AWX 0.0.1.dev (open)'} + backend = webhook_backend.WebhookBackend('POST', None, fail_silently=True) + message = EmailMessage( + 'test subject', + {'text': 'test body'}, + [], + [ + 'http://example.com', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + # Should make exactly 1 request + assert requests_mock.post.call_count == 1 + # The error message should be logged + logger_mock.error.assert_called_once() + error_call_args = logger_mock.error.call_args[0][0] + assert "Error sending webhook notification: 404" in error_call_args + assert sent_messages == 0 diff --git a/awx/main/tests/unit/scheduler/test_dag_simple.py b/awx/main/tests/unit/scheduler/test_dag_simple.py index 4bb141815754..1cea21e92d27 100644 --- a/awx/main/tests/unit/scheduler/test_dag_simple.py +++ b/awx/main/tests/unit/scheduler/test_dag_simple.py @@ -39,6 +39,6 @@ def simple_cycle_1(node_generator): def test_has_cycle(simple_cycle_1): - (g, nodes) = simple_cycle_1 + g, nodes = simple_cycle_1 assert g.has_cycle() is True diff --git a/awx/main/tests/unit/scheduler/test_dag_workflow.py b/awx/main/tests/unit/scheduler/test_dag_workflow.py index a3225b76a3e7..b4681e6b90a8 100644 --- a/awx/main/tests/unit/scheduler/test_dag_workflow.py +++ b/awx/main/tests/unit/scheduler/test_dag_workflow.py @@ -86,13 +86,13 @@ def workflow_dag_root_children(self, wf_node_generator): return (g, wf_root_nodes, wf_leaf_nodes) def test_get_root_nodes(self, workflow_dag_root_children): - (g, wf_root_nodes, ignore) = workflow_dag_root_children + g, wf_root_nodes, ignore = workflow_dag_root_children assert set([n.id for n in wf_root_nodes]) == set([n['node_object'].id for n in g.get_root_nodes()]) class TestDNR: def test_mark_dnr_nodes(self, workflow_dag_1): - (g, nodes) = workflow_dag_1 + g, nodes = workflow_dag_1 r''' 0 @@ -166,7 +166,7 @@ def simple_all_convergence(self, wf_node_generator): return (g, nodes) def test_simple_all_convergence(self, simple_all_convergence): - (g, nodes) = simple_all_convergence + g, nodes = simple_all_convergence dnr_nodes = g.mark_dnr_nodes() assert 0 == len(dnr_nodes), "no nodes should be marked DNR" @@ -197,7 +197,7 @@ def workflow_all_converge_1(self, wf_node_generator): return (g, nodes) def test_all_converge_edge_case_1(self, workflow_all_converge_1): - (g, nodes) = workflow_all_converge_1 + g, nodes = workflow_all_converge_1 dnr_nodes = g.mark_dnr_nodes() assert 2 == len(dnr_nodes), "node[1] and node[2] should be marked DNR" assert nodes[1] == dnr_nodes[0], "Node 1 should be marked DNR" @@ -233,7 +233,7 @@ def workflow_all_converge_2(self, wf_node_generator): return (g, nodes) def test_all_converge_edge_case_2(self, workflow_all_converge_2): - (g, nodes) = workflow_all_converge_2 + g, nodes = workflow_all_converge_2 dnr_nodes = g.mark_dnr_nodes() assert 1 == len(dnr_nodes), "1 and only 1 node should be marked DNR" assert nodes[2] == dnr_nodes[0], "Node 3 should be marked DNR" @@ -268,7 +268,7 @@ def workflow_all_converge_will_run(self, wf_node_generator): return (g, nodes) def test_workflow_all_converge_will_run(self, workflow_all_converge_will_run): - (g, nodes) = workflow_all_converge_will_run + g, nodes = workflow_all_converge_will_run dnr_nodes = g.mark_dnr_nodes() assert 0 == len(dnr_nodes), "No nodes should get marked DNR" @@ -306,7 +306,7 @@ def workflow_all_converge_dnr(self, wf_node_generator): return (g, nodes) def test_workflow_all_converge_while_parent_runs(self, workflow_all_converge_dnr): - (g, nodes) = workflow_all_converge_dnr + g, nodes = workflow_all_converge_dnr dnr_nodes = g.mark_dnr_nodes() assert 0 == len(dnr_nodes), "No nodes should get marked DNR" @@ -315,7 +315,7 @@ def test_workflow_all_converge_while_parent_runs(self, workflow_all_converge_dnr def test_workflow_all_converge_with_incorrect_parent(self, workflow_all_converge_dnr): # Another tick of the scheduler - (g, nodes) = workflow_all_converge_dnr + g, nodes = workflow_all_converge_dnr nodes[1].job.status = 'successful' dnr_nodes = g.mark_dnr_nodes() assert 1 == len(dnr_nodes), "1 and only 1 node should be marked DNR" @@ -326,7 +326,7 @@ def test_workflow_all_converge_with_incorrect_parent(self, workflow_all_converge def test_workflow_all_converge_runs(self, workflow_all_converge_dnr): # Trick the scheduler again to make sure the convergence node acutally runs - (g, nodes) = workflow_all_converge_dnr + g, nodes = workflow_all_converge_dnr nodes[1].job.status = 'failed' dnr_nodes = g.mark_dnr_nodes() assert 0 == len(dnr_nodes), "No nodes should be marked DNR" @@ -375,7 +375,7 @@ def workflow_all_converge_deep_dnr_tree(self, wf_node_generator): return (g, nodes) def test_workflow_all_converge_deep_dnr_tree(self, workflow_all_converge_deep_dnr_tree): - (g, nodes) = workflow_all_converge_deep_dnr_tree + g, nodes = workflow_all_converge_deep_dnr_tree dnr_nodes = g.mark_dnr_nodes() assert 4 == len(dnr_nodes), "All nodes w/ no jobs should be marked DNR" @@ -391,7 +391,7 @@ def test_workflow_all_converge_deep_dnr_tree(self, workflow_all_converge_deep_dn class TestIsWorkflowDone: @pytest.fixture def workflow_dag_2(self, workflow_dag_1): - (g, nodes) = workflow_dag_1 + g, nodes = workflow_dag_1 r''' S0 /\ @@ -416,7 +416,7 @@ def workflow_dag_2(self, workflow_dag_1): @pytest.fixture def workflow_dag_failed(self, workflow_dag_1): - (g, nodes) = workflow_dag_1 + g, nodes = workflow_dag_1 r''' S0 /\ @@ -453,7 +453,7 @@ def workflow_dag_canceled(self, wf_node_generator): @pytest.fixture def workflow_dag_failure(self, workflow_dag_canceled): - (g, nodes) = workflow_dag_canceled + g, nodes = workflow_dag_canceled nodes[0].job.status = 'failed' return (g, nodes) @@ -463,7 +463,7 @@ def test_done(self, workflow_dag_2): assert g.is_workflow_done() is False def test_workflow_done_and_failed(self, workflow_dag_failed): - (g, nodes) = workflow_dag_failed + g, nodes = workflow_dag_failed assert g.is_workflow_done() is True assert g.has_workflow_failed() == ( @@ -477,7 +477,7 @@ def test_workflow_done_and_failed(self, workflow_dag_failed): ) def test_is_workflow_done_no_unified_job_tempalte_end(self, workflow_dag_failed): - (g, nodes) = workflow_dag_failed + g, nodes = workflow_dag_failed nodes[2].unified_job_template = None @@ -492,7 +492,7 @@ def test_is_workflow_done_no_unified_job_tempalte_end(self, workflow_dag_failed) ) def test_is_workflow_done_no_unified_job_tempalte_begin(self, workflow_dag_1): - (g, nodes) = workflow_dag_1 + g, nodes = workflow_dag_1 nodes[0].unified_job_template = None g.mark_dnr_nodes() @@ -508,7 +508,7 @@ def test_is_workflow_done_no_unified_job_tempalte_begin(self, workflow_dag_1): ) def test_canceled_should_fail(self, workflow_dag_canceled): - (g, nodes) = workflow_dag_canceled + g, nodes = workflow_dag_canceled assert g.has_workflow_failed() == ( True, @@ -521,7 +521,7 @@ def test_canceled_should_fail(self, workflow_dag_canceled): ) def test_failure_should_fail(self, workflow_dag_failure): - (g, nodes) = workflow_dag_failure + g, nodes = workflow_dag_failure assert g.has_workflow_failed() == ( True, @@ -555,13 +555,13 @@ def workflow_dag_canceled(self, wf_node_generator): return (g, nodes) def test_cancel_still_runs_children(self, workflow_dag_canceled): - (g, nodes) = workflow_dag_canceled + g, nodes = workflow_dag_canceled g.mark_dnr_nodes() assert set([nodes[1], nodes[2]]) == set(g.bfs_nodes_to_run()) -@pytest.mark.skip(reason="Run manually to re-generate doc images") +@pytest.mark.xfail(reason="Run manually to re-generate doc images") class TestDocsExample: @pytest.fixture def complex_dag(self, wf_node_generator): @@ -587,7 +587,7 @@ def complex_dag(self, wf_node_generator): return (g, nodes) def test_dnr_step(self, complex_dag): - (g, nodes) = complex_dag + g, nodes = complex_dag base_dir = '/awx_devel' g.generate_graphviz_plot(file_name=os.path.join(base_dir, "workflow_step0.gv")) diff --git a/awx/main/tests/unit/settings/test_defaults.py b/awx/main/tests/unit/settings/test_defaults.py index a7f5eeeca8db..10cb5561a7f4 100644 --- a/awx/main/tests/unit/settings/test_defaults.py +++ b/awx/main/tests/unit/settings/test_defaults.py @@ -1,20 +1,19 @@ import pytest from django.conf import settings -from datetime import timedelta @pytest.mark.parametrize( - "job_name,function_path", + "task_name", [ - ('tower_scheduler', 'awx.main.tasks.system.awx_periodic_scheduler'), + 'awx.main.tasks.system.awx_periodic_scheduler', ], ) -def test_CELERYBEAT_SCHEDULE(mocker, job_name, function_path): - assert job_name in settings.CELERYBEAT_SCHEDULE - assert 'schedule' in settings.CELERYBEAT_SCHEDULE[job_name] - assert type(settings.CELERYBEAT_SCHEDULE[job_name]['schedule']) is timedelta - assert settings.CELERYBEAT_SCHEDULE[job_name]['task'] == function_path +def test_DISPATCHER_SCHEDULE(mocker, task_name): + assert task_name in settings.DISPATCHER_SCHEDULE + assert 'schedule' in settings.DISPATCHER_SCHEDULE[task_name] + assert type(settings.DISPATCHER_SCHEDULE[task_name]['schedule']) in (int, float) + assert settings.DISPATCHER_SCHEDULE[task_name]['task'] == task_name # Ensures that the function exists - mocker.patch(function_path) + mocker.patch(task_name) diff --git a/awx/main/tests/unit/settings/test_k8s_resource_setttings.py b/awx/main/tests/unit/settings/test_k8s_resource_setttings.py index a2899a8561fe..65fa45d95a10 100644 --- a/awx/main/tests/unit/settings/test_k8s_resource_setttings.py +++ b/awx/main/tests/unit/settings/test_k8s_resource_setttings.py @@ -36,7 +36,9 @@ def test_SYSTEM_TASK_ABS_MEM_conversion(value, converted_value, mem_capacity): mock_settings.IS_K8S = True assert convert_mem_str_to_bytes(value) == converted_value assert get_corrected_memory(-1) == converted_value - assert get_mem_effective_capacity(-1) == mem_capacity + assert get_mem_effective_capacity(1, is_control_node=True) == mem_capacity + # SYSTEM_TASK_ABS_MEM should not effect memory and capacity for execution nodes + assert get_mem_effective_capacity(2147483648, is_control_node=False) == 20 @pytest.mark.parametrize( @@ -58,4 +60,6 @@ def test_SYSTEM_TASK_ABS_CPU_conversion(value, converted_value, cpu_capacity): mock_settings.SYSTEM_TASK_FORKS_CPU = 4 assert convert_cpu_str_to_decimal_cpu(value) == converted_value assert get_corrected_cpu(-1) == converted_value - assert get_cpu_effective_capacity(-1) == cpu_capacity + assert get_cpu_effective_capacity(-1, is_control_node=True) == cpu_capacity + # SYSTEM_TASK_ABS_CPU should not effect cpu count and capacity for execution nodes + assert get_cpu_effective_capacity(2.0, is_control_node=False) == 8 diff --git a/awx/main/tests/unit/tasks/test_host_indirect_unit.py b/awx/main/tests/unit/tasks/test_host_indirect_unit.py new file mode 100644 index 000000000000..2b128ca6fafe --- /dev/null +++ b/awx/main/tests/unit/tasks/test_host_indirect_unit.py @@ -0,0 +1,56 @@ +import copy + +import pytest + +from awx.main.tasks.host_indirect import get_hashable_form + + +class TestHashableForm: + @pytest.mark.parametrize( + 'data', + [ + {'a': 'b'}, + ['a', 'b'], + ('a', 'b'), + {'a': {'b': 'c'}}, + {'a': ['b', 'c']}, + {'a': ('b', 'c')}, + ['a', ['b', 'c']], + ['a', ('b', 'c')], + ['a', {'b': 'c'}], + ], + ) + def test_compare_equal_data(self, data): + other_data = copy.deepcopy(data) + # A tuple of scalars may be cached so ids could legitimately be the same + if data != ('a', 'b'): + assert id(data) != id(other_data) # sanity + assert id(get_hashable_form(data)) != id(get_hashable_form(data)) + + assert get_hashable_form(data) == get_hashable_form(data) + assert hash(get_hashable_form(data)) == hash(get_hashable_form(data)) + + assert get_hashable_form(data) in {get_hashable_form(data): 1} # test lookup hit + + @pytest.mark.parametrize( + 'data, other_data', + [ + [{'a': 'b'}, {'a': 'c'}], + [{'a': 'b'}, {'a': 'b', 'c': 'd'}], + [['a', 'b'], ['a', 'c']], + [('a', 'b'), ('a', 'c')], + [{'a': {'b': 'c'}}, {'a': {'b': 'd'}}], + [{'a': ['b', 'c']}, {'a': ['b', 'd']}], + [{'a': ('b', 'c')}, {'a': ('b', 'd')}], + [['a', ['b', 'c']], ['a', ['b', 'd']]], + [['a', ('b', 'c')], ['a', ('b', 'd')]], + [['a', {'b': 'c'}], ['a', {'b': 'd'}]], + ], + ) + def test_compare_different_data(self, data, other_data): + assert data != other_data # sanity, otherwise why test this? + assert get_hashable_form(data) != get_hashable_form(other_data) + assert hash(get_hashable_form(data)) != hash(get_hashable_form(other_data)) + + assert get_hashable_form(other_data) not in {get_hashable_form(data): 1} # test lookup miss + assert get_hashable_form(data) not in {get_hashable_form(other_data): 1} diff --git a/awx/main/tests/unit/tasks/test_jobs.py b/awx/main/tests/unit/tasks/test_jobs.py new file mode 100644 index 000000000000..bcc6f4d0fd52 --- /dev/null +++ b/awx/main/tests/unit/tasks/test_jobs.py @@ -0,0 +1,592 @@ +# -*- coding: utf-8 -*- +import pytest +from unittest import mock + +from awx.main.models import ( + Inventory, + Host, +) + +from django.utils.timezone import now +from django.db.models.query import QuerySet + +from awx.main.models import ( + Job, + Organization, + Project, + JobTemplate, + UnifiedJobTemplate, + InstanceGroup, + ExecutionEnvironment, + ProjectUpdate, + InventoryUpdate, + InventorySource, + AdHocCommand, +) +from awx.main.tasks import jobs +from ansible_base.lib.workload_identity.controller import AutomationControllerJobScope + + +@pytest.fixture +def private_data_dir(tmp_path): + private_data = tmp_path / 'awx_pdd' + private_data.mkdir() + for subfolder in ('inventory', 'env'): + (private_data / subfolder).mkdir() + return str(private_data) + + +@pytest.fixture +def job_template_with_credentials(): + """ + Factory fixture that creates a job template with specified credentials. + + Usage: + job = job_template_with_credentials(ssh_cred, vault_cred) + """ + + def _create_job_template( + *credentials, org_name='test-org', project_name='test-project', inventory_name='test-inventory', jt_name='test-jt', playbook='test.yml' + ): + """ + Create a job template with the given credentials. + + Args: + *credentials: Variable number of Credential objects to attach to the job template + org_name: Name for the organization + project_name: Name for the project + inventory_name: Name for the inventory + jt_name: Name for the job template + playbook: Playbook filename + + Returns: + Job instance created from the job template + """ + org = Organization.objects.create(name=org_name) + proj = Project.objects.create(name=project_name, organization=org) + inv = Inventory.objects.create(name=inventory_name, organization=org) + jt = JobTemplate.objects.create(name=jt_name, project=proj, inventory=inv, playbook=playbook) + + if credentials: + jt.credentials.add(*credentials) + + return jt.create_unified_job() + + return _create_job_template + + +@mock.patch('awx.main.tasks.facts.settings') +@mock.patch('awx.main.tasks.jobs.create_partition', return_value=True) +def test_pre_post_run_hook_facts(mock_create_partition, mock_facts_settings, private_data_dir, execution_environment): + # Create mocked inventory and host queryset + inventory = mock.MagicMock(spec=Inventory, pk=1, kind='') + host1 = mock.MagicMock(spec=Host, id=1, name='host1', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=now(), inventory=inventory) + host2 = mock.MagicMock(spec=Host, id=2, name='host2', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=now(), inventory=inventory) + + # Mock hosts queryset + hosts = [host1, host2] + qs_hosts = mock.MagicMock(spec=QuerySet) + qs_hosts._result_cache = hosts + qs_hosts.only.return_value = hosts + qs_hosts.count.side_effect = lambda: len(qs_hosts._result_cache) + inventory.hosts = qs_hosts + + # Create mocked job object + org = mock.MagicMock(spec=Organization, pk=1) + proj = mock.MagicMock(spec=Project, pk=1, organization=org) + job = mock.MagicMock( + spec=Job, + pk=1, + id=1, + use_fact_cache=True, + project=proj, + organization=org, + job_slice_number=1, + job_slice_count=1, + inventory=inventory, + inventory_id=inventory.pk, + created=now(), + execution_environment=execution_environment, + ) + job.get_hosts_for_fact_cache = Job.get_hosts_for_fact_cache.__get__(job) + job.job_env.get = mock.MagicMock(return_value=private_data_dir) + + # Mock RunJob task + mock_facts_settings.ANSIBLE_FACT_CACHE_TIMEOUT = False + task = jobs.RunJob() + task.instance = job + task.update_model = mock.Mock(return_value=job) + task.model.objects.get = mock.Mock(return_value=job) + + # Run pre_run_hook + task.facts_write_time = task.pre_run_hook(job, private_data_dir) + + # Add a third mocked host + host3 = mock.MagicMock(spec=Host, id=3, name='host3', ansible_facts={"added": True}, ansible_facts_modified=now(), inventory=inventory) + qs_hosts._result_cache.append(host3) + assert inventory.hosts.count() == 3 + + # Run post_run_hook + task.runner_callback.artifacts_processed = mock.MagicMock(return_value=True) + task.post_run_hook(job, "success") + + # Verify final host facts + assert qs_hosts._result_cache[2].ansible_facts == {"added": True} + + +@mock.patch('awx.main.tasks.facts.bulk_update_sorted_by_id') +@mock.patch('awx.main.tasks.facts.settings') +@mock.patch('awx.main.tasks.jobs.create_partition', return_value=True) +def test_pre_post_run_hook_facts_deleted_sliced( + mock_create_partition, mock_facts_settings, mock_bulk_update_sorted_by_id, private_data_dir, execution_environment +): + # Fully mocked inventory + mock_inventory = mock.MagicMock(spec=Inventory, pk=1, kind='') + + # Create 999 mocked Host instances + hosts = [] + for i in range(999): + host = mock.MagicMock(spec=Host) + host.id = i + host.name = f'host{i}' + host.ansible_facts = {"a": 1, "b": 2} + host.ansible_facts_modified = now() + host.inventory = mock_inventory + hosts.append(host) + + # Mock inventory.hosts behavior + mock_qs_hosts = mock.MagicMock() + mock_qs_hosts.only.return_value = hosts + mock_qs_hosts.count.return_value = 999 + mock_inventory.hosts = mock_qs_hosts + + # Mock Organization and Project + org = mock.MagicMock(spec=Organization) + proj = mock.MagicMock(spec=Project) + proj.organization = org + + # Mock job object + job = mock.MagicMock(spec=Job) + job.pk = 2 + job.id = 2 + job.use_fact_cache = True + job.project = proj + job.organization = org + job.job_slice_number = 1 + job.job_slice_count = 3 + job.execution_environment = execution_environment + job.inventory = mock_inventory + job.inventory_id = mock_inventory.pk + job.created = now() + job.job_env.get.return_value = private_data_dir + + # Bind actual method for host filtering + job.get_hosts_for_fact_cache = Job.get_hosts_for_fact_cache.__get__(job) + + # Mock task instance + mock_facts_settings.ANSIBLE_FACT_CACHE_TIMEOUT = False + task = jobs.RunJob() + task.instance = job + task.update_model = mock.Mock(return_value=job) + task.model.objects.get = mock.Mock(return_value=job) + + # Call pre_run_hook + task.facts_write_time = task.pre_run_hook(job, private_data_dir) + + # Simulate one host deletion + hosts.pop(1) + mock_qs_hosts.count.return_value = 998 + + # Call post_run_hook + task.runner_callback.artifacts_processed = mock.MagicMock(return_value=True) + task.post_run_hook(job, "success") + + # Assert that ansible_facts were preserved + for host in hosts: + assert host.ansible_facts == {"a": 1, "b": 2} + + # Add expected failure cases + failures = [] + for host in hosts: + try: + assert host.ansible_facts == {"a": 1, "b": 2, "unexpected_key": "bad"} + except AssertionError: + failures.append(f"Host named {host.name} has facts {host.ansible_facts}") + + assert len(failures) > 0, f"Failures occurred for the following hosts: {failures}" + + +@mock.patch('awx.main.tasks.facts.bulk_update_sorted_by_id') +@mock.patch('awx.main.tasks.facts.settings') +def test_invalid_host_facts(mock_facts_settings, bulk_update_sorted_by_id, private_data_dir, execution_environment): + inventory = Inventory(pk=1) + mock_inventory = mock.MagicMock(spec=Inventory, wraps=inventory) + mock_inventory._state = mock.MagicMock() + + hosts = [ + Host(id=0, name='host0', ansible_facts={"a": 1, "b": 2}, ansible_facts_modified=now(), inventory=mock_inventory), + Host(id=1, name='host1', ansible_facts={"a": 1, "b": 2, "unexpected_key": "bad"}, ansible_facts_modified=now(), inventory=mock_inventory), + ] + mock_inventory.hosts = hosts + + failures = [] + for host in mock_inventory.hosts: + assert "a" in host.ansible_facts + if "unexpected_key" in host.ansible_facts: + failures.append(host.name) + + mock_facts_settings.SOME_SETTING = True + bulk_update_sorted_by_id(Host, mock_inventory.hosts, fields=['ansible_facts']) + + with pytest.raises(pytest.fail.Exception): + if failures: + pytest.fail(f" {len(failures)} facts cleared failures : {','.join(failures)}") + + +@pytest.mark.parametrize( + "job_attrs,expected_claims", + [ + ( + { + 'id': 100, + 'name': 'Test Job', + 'job_type': 'run', + 'launch_type': 'manual', + 'playbook': 'site.yml', + 'organization': Organization(id=1, name='Test Org'), + 'inventory': Inventory(id=2, name='Test Inventory'), + 'project': Project(id=3, name='Test Project'), + 'execution_environment': ExecutionEnvironment(id=4, name='Test EE'), + 'job_template': JobTemplate(id=5, name='Test Job Template'), + 'unified_job_template': UnifiedJobTemplate(pk=6, id=6, name='Test Unified Job Template'), + 'instance_group': InstanceGroup(id=7, name='Test Instance Group'), + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 100, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Test Job', + AutomationControllerJobScope.CLAIM_JOB_TYPE: 'run', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'manual', + AutomationControllerJobScope.CLAIM_PLAYBOOK_NAME: 'site.yml', + AutomationControllerJobScope.CLAIM_ORGANIZATION_NAME: 'Test Org', + AutomationControllerJobScope.CLAIM_ORGANIZATION_ID: 1, + AutomationControllerJobScope.CLAIM_INVENTORY_NAME: 'Test Inventory', + AutomationControllerJobScope.CLAIM_INVENTORY_ID: 2, + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_NAME: 'Test EE', + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_ID: 4, + AutomationControllerJobScope.CLAIM_PROJECT_NAME: 'Test Project', + AutomationControllerJobScope.CLAIM_PROJECT_ID: 3, + AutomationControllerJobScope.CLAIM_JOB_TEMPLATE_NAME: 'Test Job Template', + AutomationControllerJobScope.CLAIM_JOB_TEMPLATE_ID: 5, + AutomationControllerJobScope.CLAIM_UNIFIED_JOB_TEMPLATE_NAME: 'Test Unified Job Template', + AutomationControllerJobScope.CLAIM_UNIFIED_JOB_TEMPLATE_ID: 6, + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_NAME: 'Test Instance Group', + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_ID: 7, + }, + ), + ( + {'id': 100, 'name': 'Test', 'job_type': 'run', 'launch_type': 'manual', 'organization': Organization(id=1, name='')}, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 100, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Test', + AutomationControllerJobScope.CLAIM_JOB_TYPE: 'run', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'manual', + AutomationControllerJobScope.CLAIM_ORGANIZATION_ID: 1, + AutomationControllerJobScope.CLAIM_ORGANIZATION_NAME: '', + AutomationControllerJobScope.CLAIM_PLAYBOOK_NAME: '', + }, + ), + ], +) +def test_populate_claims_for_workload(job_attrs, expected_claims): + job = Job() + + for attr, value in job_attrs.items(): + setattr(job, attr, value) + + claims = jobs.populate_claims_for_workload(job) + assert claims == expected_claims + + +@pytest.mark.parametrize( + "workload_attrs,expected_claims", + [ + ( + { + 'id': 200, + 'name': 'Git Sync', + 'job_type': 'check', + 'launch_type': 'sync', + 'organization': Organization(id=1, name='Test Org'), + 'project': Project(pk=3, id=3, name='Test Project'), + 'unified_job_template': Project(pk=3, id=3, name='Test Project'), + 'execution_environment': ExecutionEnvironment(id=4, name='Test EE'), + 'instance_group': InstanceGroup(id=7, name='Test Instance Group'), + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 200, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Git Sync', + AutomationControllerJobScope.CLAIM_JOB_TYPE: 'check', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'sync', + AutomationControllerJobScope.CLAIM_LAUNCHED_BY_NAME: 'Test Project', + AutomationControllerJobScope.CLAIM_LAUNCHED_BY_ID: 3, + AutomationControllerJobScope.CLAIM_ORGANIZATION_NAME: 'Test Org', + AutomationControllerJobScope.CLAIM_ORGANIZATION_ID: 1, + AutomationControllerJobScope.CLAIM_PROJECT_NAME: 'Test Project', + AutomationControllerJobScope.CLAIM_PROJECT_ID: 3, + AutomationControllerJobScope.CLAIM_UNIFIED_JOB_TEMPLATE_NAME: 'Test Project', + AutomationControllerJobScope.CLAIM_UNIFIED_JOB_TEMPLATE_ID: 3, + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_NAME: 'Test EE', + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_ID: 4, + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_NAME: 'Test Instance Group', + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_ID: 7, + }, + ), + ( + { + 'id': 201, + 'name': 'Minimal Project Update', + 'job_type': 'run', + 'launch_type': 'manual', + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 201, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Minimal Project Update', + AutomationControllerJobScope.CLAIM_JOB_TYPE: 'run', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'manual', + }, + ), + ], +) +def test_populate_claims_for_project_update(workload_attrs, expected_claims): + project_update = ProjectUpdate() + for attr, value in workload_attrs.items(): + setattr(project_update, attr, value) + + claims = jobs.populate_claims_for_workload(project_update) + assert claims == expected_claims + + +@pytest.mark.parametrize( + "workload_attrs,expected_claims", + [ + ( + { + 'id': 300, + 'name': 'AWS Sync', + 'launch_type': 'scheduled', + 'organization': Organization(id=1, name='Test Org'), + 'inventory': Inventory(id=2, name='AWS Inventory'), + 'unified_job_template': InventorySource(pk=8, id=8, name='AWS Source'), + 'execution_environment': ExecutionEnvironment(id=4, name='Test EE'), + 'instance_group': InstanceGroup(id=7, name='Test Instance Group'), + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 300, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'AWS Sync', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'scheduled', + AutomationControllerJobScope.CLAIM_ORGANIZATION_NAME: 'Test Org', + AutomationControllerJobScope.CLAIM_ORGANIZATION_ID: 1, + AutomationControllerJobScope.CLAIM_INVENTORY_NAME: 'AWS Inventory', + AutomationControllerJobScope.CLAIM_INVENTORY_ID: 2, + AutomationControllerJobScope.CLAIM_UNIFIED_JOB_TEMPLATE_NAME: 'AWS Source', + AutomationControllerJobScope.CLAIM_UNIFIED_JOB_TEMPLATE_ID: 8, + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_NAME: 'Test EE', + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_ID: 4, + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_NAME: 'Test Instance Group', + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_ID: 7, + }, + ), + ( + { + 'id': 301, + 'name': 'Minimal Inventory Update', + 'launch_type': 'manual', + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 301, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Minimal Inventory Update', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'manual', + }, + ), + ], +) +def test_populate_claims_for_inventory_update(workload_attrs, expected_claims): + inventory_update = InventoryUpdate() + for attr, value in workload_attrs.items(): + setattr(inventory_update, attr, value) + + claims = jobs.populate_claims_for_workload(inventory_update) + assert claims == expected_claims + + +@pytest.mark.parametrize( + "workload_attrs,expected_claims", + [ + ( + { + 'id': 400, + 'name': 'Ping All Hosts', + 'job_type': 'run', + 'launch_type': 'manual', + 'organization': Organization(id=1, name='Test Org'), + 'inventory': Inventory(id=2, name='Test Inventory'), + 'execution_environment': ExecutionEnvironment(id=4, name='Test EE'), + 'instance_group': InstanceGroup(id=7, name='Test Instance Group'), + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 400, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Ping All Hosts', + AutomationControllerJobScope.CLAIM_JOB_TYPE: 'run', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'manual', + AutomationControllerJobScope.CLAIM_ORGANIZATION_NAME: 'Test Org', + AutomationControllerJobScope.CLAIM_ORGANIZATION_ID: 1, + AutomationControllerJobScope.CLAIM_INVENTORY_NAME: 'Test Inventory', + AutomationControllerJobScope.CLAIM_INVENTORY_ID: 2, + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_NAME: 'Test EE', + AutomationControllerJobScope.CLAIM_EXECUTION_ENVIRONMENT_ID: 4, + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_NAME: 'Test Instance Group', + AutomationControllerJobScope.CLAIM_INSTANCE_GROUP_ID: 7, + }, + ), + ( + { + 'id': 401, + 'name': 'Minimal Ad Hoc', + 'job_type': 'run', + 'launch_type': 'manual', + }, + { + AutomationControllerJobScope.CLAIM_JOB_ID: 401, + AutomationControllerJobScope.CLAIM_JOB_NAME: 'Minimal Ad Hoc', + AutomationControllerJobScope.CLAIM_JOB_TYPE: 'run', + AutomationControllerJobScope.CLAIM_LAUNCH_TYPE: 'manual', + }, + ), + ], +) +def test_populate_claims_for_adhoc_command(workload_attrs, expected_claims): + adhoc_command = AdHocCommand() + for attr, value in workload_attrs.items(): + setattr(adhoc_command, attr, value) + + claims = jobs.populate_claims_for_workload(adhoc_command) + assert claims == expected_claims + + +@mock.patch('awx.main.tasks.jobs.get_workload_identity_client') +def test_retrieve_workload_identity_jwt_returns_jwt_from_client(mock_get_client): + """retrieve_workload_identity_jwt returns the JWT string from the client.""" + mock_client = mock.MagicMock() + mock_response = mock.MagicMock() + mock_response.jwt = 'eyJ.test.jwt' + mock_client.request_workload_jwt.return_value = mock_response + mock_get_client.return_value = mock_client + + unified_job = Job() + unified_job.id = 42 + unified_job.name = 'Test Job' + unified_job.launch_type = 'manual' + unified_job.organization = Organization(id=1, name='Test Org') + unified_job.unified_job_template = None + unified_job.instance_group = None + + result = jobs.retrieve_workload_identity_jwt(unified_job, audience='https://api.example.com', scope='aap_controller_automation_job') + + assert result == 'eyJ.test.jwt' + mock_client.request_workload_jwt.assert_called_once() + call_kwargs = mock_client.request_workload_jwt.call_args[1] + assert call_kwargs['audience'] == 'https://api.example.com' + assert call_kwargs['scope'] == 'aap_controller_automation_job' + assert 'claims' in call_kwargs + assert call_kwargs['claims'][AutomationControllerJobScope.CLAIM_JOB_ID] == 42 + assert call_kwargs['claims'][AutomationControllerJobScope.CLAIM_JOB_NAME] == 'Test Job' + + +@mock.patch('awx.main.tasks.jobs.get_workload_identity_client') +def test_retrieve_workload_identity_jwt_passes_audience_and_scope(mock_get_client): + """retrieve_workload_identity_jwt passes audience and scope to the client.""" + mock_client = mock.MagicMock() + mock_client.request_workload_jwt.return_value = mock.MagicMock(jwt='token') + mock_get_client.return_value = mock_client + + unified_job = mock.MagicMock() + audience = 'custom_audience' + scope = 'custom_scope' + with mock.patch('awx.main.tasks.jobs.populate_claims_for_workload', return_value={'job_id': 1}): + jobs.retrieve_workload_identity_jwt(unified_job, audience=audience, scope=scope) + + mock_client.request_workload_jwt.assert_called_once_with(claims={'job_id': 1}, scope=scope, audience=audience) + + +@mock.patch('awx.main.tasks.jobs.get_workload_identity_client') +def test_retrieve_workload_identity_jwt_passes_workload_ttl(mock_get_client): + """retrieve_workload_identity_jwt passes workload_ttl_seconds when provided.""" + mock_client = mock.Mock() + mock_client.request_workload_jwt.return_value = mock.Mock(jwt='token') + mock_get_client.return_value = mock_client + + unified_job = mock.MagicMock() + with mock.patch('awx.main.tasks.jobs.populate_claims_for_workload', return_value={'job_id': 1}): + jobs.retrieve_workload_identity_jwt( + unified_job, + audience='https://vault.example.com', + scope='aap_controller_automation_job', + workload_ttl_seconds=3600, + ) + + mock_client.request_workload_jwt.assert_called_once_with( + claims={'job_id': 1}, + scope='aap_controller_automation_job', + audience='https://vault.example.com', + workload_ttl_seconds=3600, + ) + + +@mock.patch('awx.main.tasks.jobs.get_workload_identity_client') +def test_retrieve_workload_identity_jwt_raises_when_client_not_configured(mock_get_client): + """retrieve_workload_identity_jwt raises RuntimeError when client is None.""" + mock_get_client.return_value = None + + unified_job = mock.MagicMock() + + with pytest.raises(RuntimeError, match="Workload identity client is not configured"): + jobs.retrieve_workload_identity_jwt(unified_job, audience='test_audience', scope='test_scope') + + +@pytest.mark.parametrize('effective_timeout,expected_ttl', [(3600, 3600), (0, None)]) +@mock.patch('awx.main.tasks.jobs.retrieve_workload_identity_jwt') +@mock.patch('awx.main.tasks.jobs.flag_enabled', return_value=True) +def test_populate_workload_identity_tokens_passes_get_instance_timeout_to_client(mock_flag_enabled, mock_retrieve_jwt, effective_timeout, expected_ttl): + """populate_workload_identity_tokens passes get_instance_timeout() value as workload_ttl_seconds to retrieve_workload_identity_jwt.""" + mock_retrieve_jwt.return_value = 'eyJ.test.jwt' + + task = jobs.RunJob() + task.instance = mock.MagicMock() + + # Minimal credential with workload identity input source + credential_ctx = {} + input_src = mock.MagicMock() + input_src.pk = 1 + input_src.source_credential = mock.MagicMock() + input_src.source_credential.get_input.return_value = 'https://vault.example.com' + input_src.source_credential.name = 'vault-cred' + input_src.source_credential.credential_type = mock.MagicMock() + input_src.source_credential.credential_type.inputs = {'fields': [{'id': 'workload_identity_token', 'internal': True}]} + + credential = mock.MagicMock() + credential.context = credential_ctx + credential.input_sources = mock.MagicMock() + credential.input_sources.all.return_value = [input_src] + + task._credentials = [credential] + + with mock.patch.object(task, 'get_instance_timeout', return_value=effective_timeout): + task.populate_workload_identity_tokens() + + mock_flag_enabled.assert_called_once_with("FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED") + mock_retrieve_jwt.assert_called_once_with( + task.instance, + audience='https://vault.example.com', + scope=AutomationControllerJobScope.name, + workload_ttl_seconds=expected_ttl, + ) diff --git a/awx/main/tests/unit/tasks/test_signals.py b/awx/main/tests/unit/tasks/test_signals.py index a435b8a66039..f089ea749da9 100644 --- a/awx/main/tests/unit/tasks/test_signals.py +++ b/awx/main/tests/unit/tasks/test_signals.py @@ -1,8 +1,51 @@ import signal +import functools from awx.main.tasks.signals import signal_state, signal_callback, with_signal_handling +def pytest_sigint(): + pytest_sigint.called_count += 1 + + +def pytest_sigterm(): + pytest_sigterm.called_count += 1 + + +def pytest_sigusr1(): + pytest_sigusr1.called_count += 1 + + +def tmp_signals_for_test(func): + """ + When we run our internal signal handlers, it will call the original signal + handlers when its own work is finished. + This would crash the test runners normally, because those methods will + shut down the process. + So this is a decorator to safely replace existing signal handlers + with new signal handlers that do nothing so that tests do not crash. + """ + + @functools.wraps(func) + def wrapper(): + original_sigterm = signal.getsignal(signal.SIGTERM) + original_sigint = signal.getsignal(signal.SIGINT) + original_sigusr1 = signal.getsignal(signal.SIGUSR1) + signal.signal(signal.SIGTERM, pytest_sigterm) + signal.signal(signal.SIGINT, pytest_sigint) + signal.signal(signal.SIGUSR1, pytest_sigusr1) + pytest_sigterm.called_count = 0 + pytest_sigint.called_count = 0 + pytest_sigusr1.called_count = 0 + func() + signal.signal(signal.SIGTERM, original_sigterm) + signal.signal(signal.SIGINT, original_sigint) + signal.signal(signal.SIGUSR1, original_sigusr1) + + return wrapper + + +@tmp_signals_for_test def test_outer_inner_signal_handling(): """ Even if the flag is set in the outer context, its value should persist in the inner context @@ -15,17 +58,24 @@ def f2(): @with_signal_handling def f1(): assert signal_callback() is False - signal_state.set_flag() + signal_state.set_signal_flag(for_signal=signal.SIGTERM) assert signal_callback() f2() original_sigterm = signal.getsignal(signal.SIGTERM) assert signal_callback() is False + assert pytest_sigterm.called_count == 0 + assert pytest_sigint.called_count == 0 + assert pytest_sigusr1.called_count == 0 f1() assert signal_callback() is False assert signal.getsignal(signal.SIGTERM) is original_sigterm + assert pytest_sigterm.called_count == 1 + assert pytest_sigint.called_count == 0 + assert pytest_sigusr1.called_count == 0 +@tmp_signals_for_test def test_inner_outer_signal_handling(): """ Even if the flag is set in the inner context, its value should persist in the outer context @@ -34,7 +84,7 @@ def test_inner_outer_signal_handling(): @with_signal_handling def f2(): assert signal_callback() is False - signal_state.set_flag() + signal_state.set_signal_flag(for_signal=signal.SIGINT) assert signal_callback() @with_signal_handling @@ -45,6 +95,33 @@ def f1(): original_sigterm = signal.getsignal(signal.SIGTERM) assert signal_callback() is False + assert pytest_sigterm.called_count == 0 + assert pytest_sigint.called_count == 0 + assert pytest_sigusr1.called_count == 0 f1() assert signal_callback() is False assert signal.getsignal(signal.SIGTERM) is original_sigterm + assert pytest_sigterm.called_count == 0 + assert pytest_sigint.called_count == 1 + assert pytest_sigusr1.called_count == 0 + + +@tmp_signals_for_test +def test_sigusr1_signal_handling(): + @with_signal_handling + def f1(): + assert signal_callback() is False + signal_state.set_signal_flag(for_signal=signal.SIGUSR1) + assert signal_callback() + + original_sigusr1 = signal.getsignal(signal.SIGUSR1) + assert signal_callback() is False + assert pytest_sigterm.called_count == 0 + assert pytest_sigint.called_count == 0 + assert pytest_sigusr1.called_count == 0 + f1() + assert signal_callback() is False + assert signal.getsignal(signal.SIGUSR1) is original_sigusr1 + assert pytest_sigterm.called_count == 0 + assert pytest_sigint.called_count == 0 + assert pytest_sigusr1.called_count == 1 diff --git a/awx/main/tests/unit/tasks/test_system.py b/awx/main/tests/unit/tasks/test_system.py new file mode 100644 index 000000000000..c567dc48336d --- /dev/null +++ b/awx/main/tests/unit/tasks/test_system.py @@ -0,0 +1,64 @@ +import pytest +from unittest.mock import MagicMock, patch +from awx.main.tasks.system import update_inventory_computed_fields +from awx.main.models import Inventory +from django.db import DatabaseError + + +@pytest.fixture +def mock_logger(): + with patch("awx.main.tasks.system.logger") as logger: + yield logger + + +@pytest.fixture +def mock_inventory(): + return MagicMock(spec=Inventory) + + +def test_update_inventory_computed_fields_existing_inventory(mock_logger, mock_inventory): + # Mocking the Inventory.objects.filter method to return a non-empty queryset + with patch("awx.main.tasks.system.Inventory.objects.filter") as mock_filter: + mock_filter.return_value.exists.return_value = True + mock_filter.return_value.__getitem__.return_value = mock_inventory + + # Mocking the update_computed_fields method + with patch.object(mock_inventory, "update_computed_fields") as mock_update_computed_fields: + update_inventory_computed_fields(1) + + # Assertions + mock_filter.assert_called_once_with(id=1) + mock_update_computed_fields.assert_called_once() + + # You can add more assertions based on your specific requirements + + +def test_update_inventory_computed_fields_missing_inventory(mock_logger): + # Mocking the Inventory.objects.filter method to return an empty queryset + with patch("awx.main.tasks.system.Inventory.objects.filter") as mock_filter: + mock_filter.return_value.exists.return_value = False + + update_inventory_computed_fields(1) + + # Assertions + mock_filter.assert_called_once_with(id=1) + mock_logger.error.assert_called_once_with("Update Inventory Computed Fields failed due to missing inventory: 1") + + +def test_update_inventory_computed_fields_database_error_nosqlstate(mock_logger, mock_inventory): + # Mocking the Inventory.objects.filter method to return a non-empty queryset + with patch("awx.main.tasks.system.Inventory.objects.filter") as mock_filter: + mock_filter.return_value.exists.return_value = True + mock_filter.return_value.__getitem__.return_value = mock_inventory + + # Mocking the update_computed_fields method + with patch.object(mock_inventory, "update_computed_fields") as mock_update_computed_fields: + # Simulating the update_computed_fields method to explicitly raise a DatabaseError + mock_update_computed_fields.side_effect = DatabaseError("Some error") + + update_inventory_computed_fields(1) + + # Assertions + mock_filter.assert_called_once_with(id=1) + mock_update_computed_fields.assert_called_once() + mock_inventory.update_computed_fields.assert_called_once() diff --git a/awx/main/tests/unit/test_access.py b/awx/main/tests/unit/test_access.py index 0059cb498400..08e1e66ab59a 100644 --- a/awx/main/tests/unit/test_access.py +++ b/awx/main/tests/unit/test_access.py @@ -5,7 +5,7 @@ from django.forms.models import model_to_dict from rest_framework.exceptions import ParseError -from awx.main.access import BaseAccess, check_superuser, JobTemplateAccess, WorkflowJobTemplateAccess, SystemJobTemplateAccess, vars_are_encrypted +from awx.main.access import BaseAccess, check_superuser, JobTemplateAccess, WorkflowJobTemplateAccess, vars_are_encrypted from awx.main.models import ( Credential, @@ -239,14 +239,3 @@ def can_copy(self, obj): foo = object() foo_capabilities = foo_access.get_user_capabilities(foo, ['edit', 'copy']) assert foo_capabilities == {'edit': 'bar', 'copy': 'foo'} - - -def test_system_job_template_can_start(mocker): - user = mocker.MagicMock(spec=User, id=1, is_system_auditor=True, is_superuser=False) - assert user.is_system_auditor - access = SystemJobTemplateAccess(user) - assert not access.can_start(None) - - user.is_superuser = True - access = SystemJobTemplateAccess(user) - assert access.can_start(None) diff --git a/awx/main/tests/unit/test_db.py b/awx/main/tests/unit/test_db.py index ce0b8bbeccb8..b1ffbfc0d8c8 100644 --- a/awx/main/tests/unit/test_db.py +++ b/awx/main/tests/unit/test_db.py @@ -9,7 +9,6 @@ import awx from awx.main.db.profiled_pg.base import RecordedQueryLog - QUERY = {'sql': 'SELECT * FROM main_job', 'time': '.01'} EXPLAIN = 'Seq Scan on public.main_job (cost=0.00..1.18 rows=18 width=86)' diff --git a/awx/main/tests/unit/test_fields.py b/awx/main/tests/unit/test_fields.py index da669ae47d2c..2250dbde70fb 100644 --- a/awx/main/tests/unit/test_fields.py +++ b/awx/main/tests/unit/test_fields.py @@ -76,6 +76,9 @@ def schema(self, model_instance): ({'fields': [{'id': 'token', 'label': 'Token', 'secret': 'bad'}]}, False), ({'fields': [{'id': 'token', 'label': 'Token', 'ask_at_runtime': True}]}, True), ({'fields': [{'id': 'token', 'label': 'Token', 'ask_at_runtime': 'bad'}]}, False), # noqa + ({'fields': [{'id': 'token', 'label': 'Token', 'internal': True}]}, True), + ({'fields': [{'id': 'token', 'label': 'Token', 'internal': False}]}, True), + ({'fields': [{'id': 'token', 'label': 'Token', 'internal': 'bad'}]}, False), ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': 'not-a-list'}]}, False), # noqa ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': []}]}, False), ({'fields': [{'id': 'become_method', 'label': 'Become', 'choices': ['su', 'sudo']}]}, True), # noqa @@ -204,6 +207,68 @@ def test_credential_creation_validation_failure(inputs): assert e.type in (ValidationError, DRFValidationError) +def test_credential_input_field_excludes_internal_fields(): + """Internal fields should be excluded from the schema generated by CredentialInputField, + preventing users from providing values for internally resolved fields.""" + type_ = CredentialType( + kind='cloud', + name='SomeCloud', + managed=True, + inputs={ + 'fields': [ + {'id': 'username', 'label': 'Username', 'type': 'string'}, + {'id': 'resolved_token', 'label': 'Token', 'type': 'string', 'internal': True}, + ] + }, + ) + cred = Credential(credential_type=type_, name="Test Credential", inputs={'username': 'joe'}) + field = cred._meta.get_field('inputs') + schema = field.schema(cred) + + assert 'username' in schema['properties'] + assert 'resolved_token' not in schema['properties'] + + +def test_credential_input_field_rejects_values_for_internal_fields(): + """Users should not be able to provide values for fields marked as internal.""" + type_ = CredentialType( + kind='cloud', + name='SomeCloud', + managed=True, + inputs={ + 'fields': [ + {'id': 'username', 'label': 'Username', 'type': 'string'}, + {'id': 'resolved_token', 'label': 'Token', 'type': 'string', 'internal': True}, + ] + }, + ) + cred = Credential(credential_type=type_, name="Test Credential", inputs={'username': 'joe', 'resolved_token': 'secret'}) + field = cred._meta.get_field('inputs') + + with pytest.raises(Exception) as e: + field.validate(cred.inputs, cred) + assert e.type in (ValidationError, DRFValidationError) + + +def test_credential_input_field_accepts_non_internal_fields_only(): + """Credentials with only non-internal field values should validate successfully.""" + type_ = CredentialType( + kind='cloud', + name='SomeCloud', + managed=True, + inputs={ + 'fields': [ + {'id': 'username', 'label': 'Username', 'type': 'string'}, + {'id': 'resolved_token', 'label': 'Token', 'type': 'string', 'internal': True}, + ] + }, + ) + cred = Credential(credential_type=type_, name="Test Credential", inputs={'username': 'joe'}) + field = cred._meta.get_field('inputs') + # Should not raise + field.validate(cred.inputs, cred) + + def test_implicit_role_field_parents(): """This assures that every ImplicitRoleField only references parents which are relationships that actually exist diff --git a/awx/main/tests/unit/test_indirect_query_discovery.py b/awx/main/tests/unit/test_indirect_query_discovery.py new file mode 100644 index 000000000000..159873614e60 --- /dev/null +++ b/awx/main/tests/unit/test_indirect_query_discovery.py @@ -0,0 +1,431 @@ +""" +Unit tests for external query discovery and version fallback logic. +Tests for AAP-58456: Unit Test Suite for External Query Handling +""" + +import sys +from io import StringIO +from unittest import mock + +import pytest +from packaging.version import Version + + +# Helper for mocking importlib.resources.files() path traversal +def create_chainable_path_mock(final_mock, depth=3): + """Mock that supports chained / operations: mock / 'a' / 'b' / 'c' -> final_mock""" + + class ChainableMock: + def __init__(self, d=0): + self.d = d + + def __truediv__(self, other): + return final_mock if self.d >= depth - 1 else ChainableMock(self.d + 1) + + return ChainableMock() + + +def create_queries_dir_mock(file_lookup_func): + """Mock for queries_dir: mock / 'filename' -> file_lookup_func('filename')""" + + class QueriesDirMock: + def __truediv__(self, filename): + return file_lookup_func(filename) + + return QueriesDirMock() + + +# Ansible mocking required for importing the module (it imports from ansible.plugins.callback.CallbackBase) +class MockCallbackBase: + def __init__(self): + self._display = mock.MagicMock() + + def v2_playbook_on_stats(self, stats): + pass + + +_mock_callback_module = mock.MagicMock() +_mock_callback_module.CallbackBase = MockCallbackBase + + +@pytest.fixture(autouse=True) +def _mock_ansible_modules(): + """Temporarily inject fake ansible modules so the callback plugin can be imported.""" + with mock.patch.dict( + sys.modules, + { + 'ansible': mock.MagicMock(), + 'ansible.plugins': mock.MagicMock(), + 'ansible.plugins.callback': _mock_callback_module, + 'ansible.cli': mock.MagicMock(), + 'ansible.cli.galaxy': mock.MagicMock(), + 'ansible.release': mock.MagicMock(__version__='2.16.0'), + 'ansible.galaxy': mock.MagicMock(), + 'ansible.galaxy.collection': mock.MagicMock(), + 'ansible.utils': mock.MagicMock(), + 'ansible.utils.collection_loader': mock.MagicMock(), + 'ansible.constants': mock.MagicMock(), + }, + ): + yield + + +class TestListExternalQueries: + """Tests for list_external_queries function.""" + + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + def test_returns_empty_when_collection_not_installed(self, mock_files): + from awx.playbooks.library.indirect_instance_count import list_external_queries + + mock_files.side_effect = ModuleNotFoundError("No module named 'ansible_collections.redhat'") + + result = list_external_queries('demo', 'external') + + assert result == [] + + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + def test_parses_version_from_filenames(self, mock_files): + from awx.playbooks.library.indirect_instance_count import list_external_queries + + mock_file_1 = mock.Mock() + mock_file_1.name = 'demo.external.1.0.0.yml' + mock_file_2 = mock.Mock() + mock_file_2.name = 'demo.external.2.1.0.yml' + mock_file_other = mock.Mock() + mock_file_other.name = 'other.collection.1.0.0.yml' + + mock_queries_dir = mock.Mock() + mock_queries_dir.iterdir.return_value = [mock_file_1, mock_file_2, mock_file_other] + mock_files.return_value = create_chainable_path_mock(mock_queries_dir) + + result = list_external_queries('demo', 'external') + + assert len(result) == 2 + assert Version('1.0.0') in result + assert Version('2.1.0') in result + + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + def test_skips_invalid_versions(self, mock_files): + from awx.playbooks.library.indirect_instance_count import list_external_queries + + mock_file_valid = mock.Mock() + mock_file_valid.name = 'demo.external.1.0.0.yml' + mock_file_invalid = mock.Mock() + mock_file_invalid.name = 'demo.external.invalid.yml' + + mock_queries_dir = mock.Mock() + mock_queries_dir.iterdir.return_value = [mock_file_valid, mock_file_invalid] + mock_files.return_value = create_chainable_path_mock(mock_queries_dir) + + result = list_external_queries('demo', 'external') + + assert len(result) == 1 + assert Version('1.0.0') in result + + +class TestVersionFallback: + """Tests for version fallback logic (AC7.4-AC7.9).""" + + @mock.patch('awx.playbooks.library.indirect_instance_count._get_query_file_dir') + def test_exact_match_preferred(self, mock_get_dir): + """AC7.4: Exact version match is preferred over fallback version.""" + from awx.playbooks.library.indirect_instance_count import find_external_query_with_fallback + + mock_exact_file = mock.Mock() + mock_exact_file.exists.return_value = True + mock_exact_file.open.return_value.__enter__ = mock.Mock(return_value=StringIO('exact_version_query')) + mock_exact_file.open.return_value.__exit__ = mock.Mock(return_value=False) + + mock_get_dir.return_value = create_queries_dir_mock(lambda f: mock_exact_file) + + content, fallback_used, version = find_external_query_with_fallback('demo', 'external', '2.5.0') + + assert content == 'exact_version_query' + assert fallback_used is False + assert version == '2.5.0' + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_external_queries') + @mock.patch('awx.playbooks.library.indirect_instance_count._get_query_file_dir') + def test_fallback_nearest_lower_same_major(self, mock_get_dir, mock_list): + """AC7.5: Fallback selects nearest lower version within same major version. + + When installed is 4.5.0 and 4.0.0/4.1.0 are available, selects 4.1.0. + """ + from awx.playbooks.library.indirect_instance_count import find_external_query_with_fallback + + mock_list.return_value = [Version('4.0.0'), Version('4.1.0')] + + mock_exact_file = mock.Mock(exists=mock.Mock(return_value=False)) + mock_fallback_file = mock.Mock() + mock_fallback_file.exists.return_value = True + mock_fallback_file.open.return_value.__enter__ = mock.Mock(return_value=StringIO('fallback_query')) + mock_fallback_file.open.return_value.__exit__ = mock.Mock(return_value=False) + + def file_lookup(filename): + return mock_fallback_file if '4.1.0' in filename else mock_exact_file + + mock_get_dir.return_value = create_queries_dir_mock(file_lookup) + + content, fallback_used, version = find_external_query_with_fallback('community', 'vmware', '4.5.0') + + assert content == 'fallback_query' + assert fallback_used is True + assert version == '4.1.0' + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_external_queries') + @mock.patch('awx.playbooks.library.indirect_instance_count._get_query_file_dir') + def test_fallback_respects_major_version_boundary(self, mock_get_dir, mock_list): + """Test that fallback does NOT cross major version boundaries. + + When installed version is 6.0.0 and only 5.0.0 query exists, + no fallback should occur because major versions differ. + """ + from awx.playbooks.library.indirect_instance_count import find_external_query_with_fallback + + mock_list.return_value = [Version('5.0.0')] + + # Mock exact file (6.0.0) to not exist + mock_exact_file = mock.Mock(exists=mock.Mock(return_value=False)) + # Mock fallback file (5.0.0) to exist - if major version check is broken, + # this file would be incorrectly selected + mock_fallback_file = mock.Mock() + mock_fallback_file.exists.return_value = True + mock_fallback_file.open.return_value.__enter__ = mock.Mock(return_value=StringIO('wrong_major_version_query')) + mock_fallback_file.open.return_value.__exit__ = mock.Mock(return_value=False) + + def file_lookup(filename): + return mock_fallback_file if '5.0.0' in filename else mock_exact_file + + mock_get_dir.return_value = create_queries_dir_mock(file_lookup) + + content, fallback_used, version = find_external_query_with_fallback('community', 'vmware', '6.0.0') + + # Should NOT fall back to 5.0.0 because major version differs (5 vs 6) + assert content is None + assert fallback_used is False + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_external_queries') + @mock.patch('awx.playbooks.library.indirect_instance_count._get_query_file_dir') + def test_no_fallback_when_incompatible(self, mock_get_dir, mock_list): + """AC7.7: No fallback when all available versions are higher than installed. + + When installed version is 3.8.0 and only 4.0.0 and 5.0.0 exist, + no fallback should occur because both are higher than installed. + """ + from awx.playbooks.library.indirect_instance_count import find_external_query_with_fallback + + mock_list.return_value = [Version('4.0.0'), Version('5.0.0')] + + # Mock exact file (3.8.0) to not exist + mock_exact_file = mock.Mock(exists=mock.Mock(return_value=False)) + # Mock available files to exist - if version filtering is broken, + # one of these would be incorrectly selected + mock_available_file = mock.Mock() + mock_available_file.exists.return_value = True + mock_available_file.open.return_value.__enter__ = mock.Mock(return_value=StringIO('higher_version_query')) + mock_available_file.open.return_value.__exit__ = mock.Mock(return_value=False) + + def file_lookup(filename): + if '4.0.0' in filename or '5.0.0' in filename: + return mock_available_file + return mock_exact_file + + mock_get_dir.return_value = create_queries_dir_mock(file_lookup) + + content, fallback_used, version = find_external_query_with_fallback('community', 'vmware', '3.8.0') + + # Should NOT fall back to 4.0.0 or 5.0.0 because both are higher than 3.8.0 + assert content is None + assert fallback_used is False + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_external_queries') + @mock.patch('awx.playbooks.library.indirect_instance_count._get_query_file_dir') + def test_fallback_selection_logic(self, mock_get_dir, mock_list): + """AC7.9: Complex fallback scenario with multiple candidates. + + When installed is 4.5.0 and 4.0.0, 4.1.0, 5.0.0 are available, + selects 4.1.0 (highest compatible within same major, <= installed). + """ + from awx.playbooks.library.indirect_instance_count import find_external_query_with_fallback + + mock_list.return_value = [Version('4.0.0'), Version('4.1.0'), Version('5.0.0')] + + mock_exact_file = mock.Mock(exists=mock.Mock(return_value=False)) + mock_fallback_file = mock.Mock() + mock_fallback_file.exists.return_value = True + mock_fallback_file.open.return_value.__enter__ = mock.Mock(return_value=StringIO('query_4.1.0')) + mock_fallback_file.open.return_value.__exit__ = mock.Mock(return_value=False) + + def file_lookup(filename): + return mock_fallback_file if '4.1.0' in filename else mock_exact_file + + mock_get_dir.return_value = create_queries_dir_mock(file_lookup) + + content, fallback_used, version = find_external_query_with_fallback('community', 'vmware', '4.5.0') + + assert version == '4.1.0' + assert fallback_used is True + assert content == 'query_4.1.0' + + +class TestExternalQueryDiscovery: + """Tests for callback plugin query discovery (AC7.1-AC7.3).""" + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) + def test_precedence_embedded_over_external(self, mock_fallback, mock_files, mock_list_collections): + """AC7.1: Embedded query takes precedence when both embedded and external exist.""" + from awx.playbooks.library.indirect_instance_count import CallbackModule + + mock_list_collections.return_value = [mock.Mock(namespace='demo', name='query', ver='1.0.0', fqcn='demo.query')] + + mock_embedded_file = mock.Mock() + mock_embedded_file.exists.return_value = True + mock_embedded_file.open.return_value.__enter__ = mock.Mock(return_value=StringIO('embedded_query')) + mock_embedded_file.open.return_value.__exit__ = mock.Mock(return_value=False) + mock_files.return_value = create_chainable_path_mock(mock_embedded_file) + + callback = CallbackModule() + callback._display = mock.Mock() + + with mock.patch('builtins.open', mock.mock_open()): + with mock.patch('json.dumps', return_value='{}'): + callback.v2_playbook_on_stats(mock.Mock()) + + mock_fallback.assert_not_called() + callback._display.vv.assert_called() + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) + def test_external_query_when_embedded_missing(self, mock_fallback, mock_files, mock_list_collections): + """AC7.2: External query is discovered when embedded query is missing.""" + from awx.playbooks.library.indirect_instance_count import CallbackModule + + mock_candidate = mock.Mock() + mock_candidate.namespace = 'demo' + mock_candidate.name = 'external' + mock_candidate.ver = '2.5.0' + mock_candidate.fqcn = 'demo.external' + mock_list_collections.return_value = [mock_candidate] + + mock_embedded_file = mock.Mock(exists=mock.Mock(return_value=False)) + mock_files.return_value = create_chainable_path_mock(mock_embedded_file) + mock_fallback.return_value = ('external_query_content', False, '2.5.0') + + callback = CallbackModule() + callback._display = mock.Mock() + + with mock.patch('builtins.open', mock.mock_open()): + with mock.patch('json.dumps', return_value='{}'): + callback.v2_playbook_on_stats(mock.Mock()) + + mock_fallback.assert_called_once_with('demo', 'external', '2.5.0') + callback._display.v.assert_called() + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) + def test_no_query_when_both_missing(self, mock_fallback, mock_files, mock_list_collections): + """AC7.3: No query is used when both embedded and external queries are missing.""" + from awx.playbooks.library.indirect_instance_count import CallbackModule + + mock_list_collections.return_value = [mock.Mock(namespace='unknown', name='collection', ver='1.0.0', fqcn='unknown.collection')] + + mock_embedded_file = mock.Mock(exists=mock.Mock(return_value=False)) + mock_files.return_value = create_chainable_path_mock(mock_embedded_file) + mock_fallback.return_value = (None, False, None) + + callback = CallbackModule() + callback._display = mock.Mock() + + with mock.patch('builtins.open', mock.mock_open()): + with mock.patch('json.dumps', return_value='{}'): + callback.v2_playbook_on_stats(mock.Mock()) + + mock_fallback.assert_called_once() + + @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') + @mock.patch('awx.playbooks.library.indirect_instance_count.files') + @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) + def test_info_log_on_fallback(self, mock_fallback, mock_files, mock_list_collections): + """AC7.8: Log message is emitted when fallback version is used. + + Verifies that when a fallback version is used, a log message is emitted + containing both the fallback version and the collection FQCN. + + Note: AC7.8 specifies 'warning logs' but implementation uses verbose/info + level (_display.v) as this is informational rather than a warning condition. + """ + from awx.playbooks.library.indirect_instance_count import CallbackModule + + mock_list_collections.return_value = [mock.Mock(namespace='community', name='vmware', ver='4.5.0', fqcn='community.vmware')] + + mock_embedded_file = mock.Mock(exists=mock.Mock(return_value=False)) + mock_files.return_value = create_chainable_path_mock(mock_embedded_file) + mock_fallback.return_value = ('fallback_query_content', True, '4.1.0') + + callback = CallbackModule() + callback._display = mock.Mock() + + with mock.patch('builtins.open', mock.mock_open()): + with mock.patch('json.dumps', return_value='{}'): + callback.v2_playbook_on_stats(mock.Mock()) + + callback._display.v.assert_called() + call_args = callback._display.v.call_args[0][0] + assert '4.1.0' in call_args + assert 'community.vmware' in call_args + + +class TestPrivateDataDirIntegration: + """Tests for vendor collection copying (AC7.10-AC7.11).""" + + @mock.patch('awx.main.tasks.jobs.flag_enabled') + @mock.patch('awx.main.tasks.jobs.shutil.copytree') + @mock.patch('awx.main.tasks.jobs.os.path.exists') + def test_vendor_collections_copied(self, mock_exists, mock_copytree, mock_flag): + """AC7.10: build_private_data_files() copies vendor collections to private_data_dir.""" + from awx.main.tasks.jobs import BaseTask + + mock_flag.return_value = True + mock_exists.return_value = True + + task = BaseTask() + task.instance = mock.Mock() + task.cleanup_paths = [] + task.build_private_data = mock.Mock(return_value=None) + + private_data_dir = '/tmp/awx_123_abc' + task.build_private_data_files(task.instance, private_data_dir) + + mock_copytree.assert_called_once_with('/var/lib/awx/vendor_collections', f'{private_data_dir}/vendor_collections') + + @mock.patch('awx.main.tasks.jobs.flag_enabled') + @mock.patch('awx.main.tasks.jobs.logger') + @mock.patch('awx.main.tasks.jobs.shutil.copytree') + @mock.patch('awx.main.tasks.jobs.os.path.exists') + def test_missing_source_handled_gracefully(self, mock_exists, mock_copytree, mock_logger, mock_flag): + """AC7.11: Collection copy handles missing source directory gracefully.""" + from awx.main.tasks.jobs import BaseTask + + mock_flag.return_value = True + mock_exists.return_value = False + + task = BaseTask() + task.instance = mock.Mock() + task.cleanup_paths = [] + task.build_private_data = mock.Mock(return_value=None) + + private_data_dir = '/tmp/awx_123_abc' + result = task.build_private_data_files(task.instance, private_data_dir) + + # copytree should not be called when source doesn't exist + mock_copytree.assert_not_called() + # Function should complete without raising an exception + assert result is not None diff --git a/awx/main/tests/unit/test_redact.py b/awx/main/tests/unit/test_redact.py index c5585ff75cda..f175cbbf7a55 100644 --- a/awx/main/tests/unit/test_redact.py +++ b/awx/main/tests/unit/test_redact.py @@ -36,8 +36,7 @@ TEST_CLEARTEXT.append( { 'uri': uri, - 'text': textwrap.dedent( - """\ + 'text': textwrap.dedent("""\ PLAY [all] ******************************************************************** TASK: [delete project directory before update] ******************************** @@ -59,9 +58,7 @@ localhost : ok=0 changed=0 unreachable=0 failed=1 - """ - % (uri.username, uri.password, str(uri), str(uri)) - ), + """ % (uri.username, uri.password, str(uri), str(uri))), 'host_occurrences': 2, } ) @@ -70,8 +67,7 @@ TEST_CLEARTEXT.append( { 'uri': uri, - 'text': textwrap.dedent( - """\ + 'text': textwrap.dedent("""\ TASK: [update project using git] ** failed: [localhost] => {"cmd": "/usr/bin/git ls-remote https://REDACTED:********", "failed": true, "rc": 128} stderr: error: Couldn't resolve host '@%s' while accessing %s @@ -81,9 +77,7 @@ msg: error: Couldn't resolve host '@%s' while accessing %s fatal: HTTP request failed - """ - % (uri.host, str(uri), uri.host, str(uri)) - ), + """ % (uri.host, str(uri), uri.host, str(uri))), 'host_occurrences': 4, } ) diff --git a/awx/main/tests/unit/test_settings.py b/awx/main/tests/unit/test_settings.py index 19b90099a175..ee517d6a870e 100644 --- a/awx/main/tests/unit/test_settings.py +++ b/awx/main/tests/unit/test_settings.py @@ -1,8 +1,93 @@ -from split_settings.tools import include +LOCAL_SETTINGS = ( + 'ALLOWED_HOSTS', + 'BROADCAST_WEBSOCKET_PORT', + 'BROADCAST_WEBSOCKET_VERIFY_CERT', + 'BROADCAST_WEBSOCKET_PROTOCOL', + 'BROADCAST_WEBSOCKET_SECRET', + 'DATABASES', + 'CACHES', + 'DEBUG', + 'NAMED_URL_GRAPH', + # Platform flags are managed by the platform flags system and have environment-specific defaults + 'FEATURE_INDIRECT_NODE_COUNTING_ENABLED', +) def test_postprocess_auth_basic_enabled(): - locals().update({'__file__': __file__}) + """The final loaded settings should have basic auth enabled.""" + from awx.settings import REST_FRAMEWORK - include('../../../settings/defaults.py', scope=locals()) - assert 'awx.api.authentication.LoggedBasicAuthentication' in locals()['REST_FRAMEWORK']['DEFAULT_AUTHENTICATION_CLASSES'] + assert 'awx.api.authentication.LoggedBasicAuthentication' in REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] + + +def test_default_settings(): + """Ensure that all default settings are present in the snapshot.""" + from django.conf import settings + + for k in dir(settings): + if k not in settings.DEFAULTS_SNAPSHOT or k in LOCAL_SETTINGS: + continue + default_val = getattr(settings.default_settings, k, None) + snapshot_val = settings.DEFAULTS_SNAPSHOT[k] + assert default_val == snapshot_val, f'Setting for {k} does not match snapshot:\nsnapshot: {snapshot_val}\ndefault: {default_val}' + + +def test_django_conf_settings_is_awx_settings(): + """Ensure that the settings loaded from dynaconf are the same as the settings delivered to django.""" + from django.conf import settings + from awx.settings import REST_FRAMEWORK + + assert settings.REST_FRAMEWORK == REST_FRAMEWORK + + +def test_dynaconf_is_awx_settings(): + """Ensure that the settings loaded from dynaconf are the same as the settings delivered to django.""" + from django.conf import settings + from awx.settings import REST_FRAMEWORK + + assert settings.DYNACONF.REST_FRAMEWORK == REST_FRAMEWORK + + +def test_development_settings_can_be_directly_imported(monkeypatch): + """Ensure that the development settings can be directly imported.""" + monkeypatch.setenv('AWX_MODE', 'development') + from django.conf import settings + from awx.settings.development import REST_FRAMEWORK + from awx.settings.development import DEBUG # actually set on defaults.py and not overridden in development.py + + assert settings.REST_FRAMEWORK == REST_FRAMEWORK + assert DEBUG is True + + +def test_merge_application_name(): + """Ensure that the merge_application_name function works as expected.""" + from awx.settings.functions import merge_application_name + + settings = { + "DATABASES__default__ENGINE": "django.db.backends.postgresql", + "CLUSTER_HOST_ID": "test-cluster-host-id", + } + result = merge_application_name(settings)["DATABASES__default__OPTIONS__application_name"] + assert result.startswith("awx-") + assert "test-cluster" in result + + +def test_development_defaults_feature_flags(monkeypatch): + """Ensure that development_defaults.py sets the correct feature flags.""" + monkeypatch.setenv('AWX_MODE', 'development') + + # Import the development_defaults module directly to trigger coverage of the new lines + import importlib.util + import os + + spec = importlib.util.spec_from_file_location("development_defaults", os.path.join(os.path.dirname(__file__), "../../../settings/development_defaults.py")) + development_defaults = importlib.util.module_from_spec(spec) + spec.loader.exec_module(development_defaults) + + # Also import through the development settings to ensure both paths are tested + from awx.settings.development import FEATURE_INDIRECT_NODE_COUNTING_ENABLED + + # Verify the feature flags are set correctly in both the module and settings + assert hasattr(development_defaults, 'FEATURE_INDIRECT_NODE_COUNTING_ENABLED') + assert development_defaults.FEATURE_INDIRECT_NODE_COUNTING_ENABLED is True + assert FEATURE_INDIRECT_NODE_COUNTING_ENABLED is True diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index bfec59b6163c..41b6ff058b08 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -1,16 +1,14 @@ # -*- coding: utf-8 -*- -import configparser import json import os -import shutil -import tempfile from pathlib import Path import fcntl from unittest import mock import pytest import yaml -import jinja2 + +from awx_plugins.interfaces._temporary_private_container_api import CONTAINER_ROOT from django.conf import settings @@ -37,7 +35,6 @@ from awx.main.tasks import jobs, system, receptor from awx.main.utils import encrypt_field, encrypt_value from awx.main.utils.safe_yaml import SafeLoader -from awx.main.utils.execution_environments import CONTAINER_ROOT from awx.main.utils.licensing import Licenser from awx.main.constants import JOB_VARIABLE_PREFIXES @@ -61,14 +58,12 @@ class TestJobExecution(object): @pytest.fixture -def private_data_dir(): - private_data = tempfile.mkdtemp(prefix='awx_') +def private_data_dir(tmp_path): + private_data = tmp_path / 'awx_pdd' + private_data.mkdir() for subfolder in ('inventory', 'env'): - runner_subfolder = os.path.join(private_data, subfolder) - if not os.path.exists(runner_subfolder): - os.mkdir(runner_subfolder) - yield private_data - shutil.rmtree(private_data, True) + (private_data / subfolder).mkdir() + return str(private_data) @pytest.fixture @@ -108,7 +103,7 @@ def job(): @pytest.fixture def adhoc_job(): - return AdHocCommand(pk=1, id=1, inventory=Inventory()) + return AdHocCommand(pk=1, id=1, inventory=Inventory(), status='waiting') @pytest.fixture @@ -137,17 +132,10 @@ def test_send_notifications_not_list(): def test_send_notifications_job_id(mocker): - with mocker.patch('awx.main.models.UnifiedJob.objects.get'): - system.send_notifications([], job_id=1) - assert UnifiedJob.objects.get.called - assert UnifiedJob.objects.get.called_with(id=1) - - -def test_work_success_callback_missing_job(): - task_data = {'type': 'project_update', 'id': 9999} - with mock.patch('django.db.models.query.QuerySet.get') as get_mock: - get_mock.side_effect = ProjectUpdate.DoesNotExist() - assert system.handle_work_success(task_data) is None + mocker.patch('awx.main.models.UnifiedJob.objects.get') + system.send_notifications([], job_id=1) + assert UnifiedJob.objects.get.called + UnifiedJob.objects.get.assert_called_with(id=1) @mock.patch('awx.main.models.UnifiedJob.objects.get') @@ -164,7 +152,7 @@ def test_send_notifications_list(mock_notifications_filter, mock_job_get, mocker assert mock_notifications[0].save.called assert mock_job.notifications.add.called - assert mock_job.notifications.add.called_with(*mock_notifications) + mock_job.notifications.add.assert_called_with(*mock_notifications) @pytest.mark.parametrize( @@ -371,7 +359,7 @@ class TestExtraVarSanitation(TestJobExecution): # are deemed trustable, because they can only be added by users w/ enough # privilege to add/modify a Job Template) - UNSAFE = '{{ lookup(' 'pipe' ',' 'ls -la' ') }}' + UNSAFE = "{{ lookup('pipe', 'ls -la') }}" def test_vars_unsafe_by_default(self, job, private_data_dir, mock_me): job.created_by = User(pk=123, username='angry-spud') @@ -380,8 +368,8 @@ def test_vars_unsafe_by_default(self, job, private_data_dir, mock_me): task = jobs.RunJob() task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) # ensure that strings are marked as unsafe for name in JOB_VARIABLE_PREFIXES: @@ -399,8 +387,8 @@ def test_launchtime_vars_unsafe(self, job, private_data_dir, mock_me): task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) assert extra_vars['msg'] == self.UNSAFE assert hasattr(extra_vars['msg'], '__UNSAFE__') @@ -410,8 +398,8 @@ def test_nested_launchtime_vars_unsafe(self, job, private_data_dir, mock_me): task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) assert extra_vars['msg'] == {'a': [self.UNSAFE]} assert hasattr(extra_vars['msg']['a'][0], '__UNSAFE__') @@ -421,8 +409,8 @@ def test_allowed_jt_extra_vars(self, job, private_data_dir, mock_me): task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) assert extra_vars['msg'] == self.UNSAFE assert not hasattr(extra_vars['msg'], '__UNSAFE__') @@ -433,8 +421,8 @@ def test_nested_allowed_vars(self, job, private_data_dir, mock_me): task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) assert extra_vars['msg'] == {'a': {'b': [self.UNSAFE]}} assert not hasattr(extra_vars['msg']['a']['b'][0], '__UNSAFE__') @@ -447,8 +435,8 @@ def test_sensitive_values_dont_leak(self, job, private_data_dir, mock_me): task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) assert extra_vars['msg'] == 'other-value' assert hasattr(extra_vars['msg'], '__UNSAFE__') @@ -462,13 +450,14 @@ def test_overwritten_jt_extra_vars(self, job, private_data_dir, mock_me): task.build_extra_vars_file(job, private_data_dir) - fd = open(os.path.join(private_data_dir, 'env', 'extravars')) - extra_vars = yaml.load(fd, Loader=SafeLoader) + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) assert extra_vars['msg'] == self.UNSAFE assert hasattr(extra_vars['msg'], '__UNSAFE__') class TestGenericRun: + @pytest.mark.django_db(reset_sequences=True) def test_generic_failure(self, patch_Job, execution_environment, mock_me, mock_create_partition): job = Job(status='running', inventory=Inventory(), project=Project(local_path='/projects/_23_foo')) job.websocket_emit_status = mock.Mock() @@ -480,7 +469,7 @@ def test_generic_failure(self, patch_Job, execution_environment, mock_me, mock_c task.model.objects.get = mock.Mock(return_value=job) task.build_private_data_files = mock.Mock(side_effect=OSError()) - with mock.patch('awx.main.tasks.jobs.shutil.copytree'): + with mock.patch('awx.main.tasks.jobs.shutil.copytree'), mock.patch('awx.main.tasks.jobs.evaluate_policy'): with pytest.raises(Exception): task.run(1) @@ -489,26 +478,6 @@ def test_generic_failure(self, patch_Job, execution_environment, mock_me, mock_c assert update_model_call['status'] == 'error' assert update_model_call['emitted_events'] == 0 - def test_cancel_flag(self, job, update_model_wrapper, execution_environment, mock_me, mock_create_partition): - job.status = 'running' - job.cancel_flag = True - job.websocket_emit_status = mock.Mock() - job.send_notification_templates = mock.Mock() - job.execution_environment = execution_environment - - task = jobs.RunJob() - task.instance = job - task.update_model = mock.Mock(wraps=update_model_wrapper) - task.model.objects.get = mock.Mock(return_value=job) - task.build_private_data_files = mock.Mock() - - with mock.patch('awx.main.tasks.jobs.shutil.copytree'): - with pytest.raises(Exception): - task.run(1) - - for c in [mock.call(1, start_args='', status='canceled')]: - assert c in task.update_model.call_args_list - def test_event_count(self, mock_me): task = jobs.RunJob() task.runner_callback.dispatcher = mock.MagicMock() @@ -573,6 +542,7 @@ def test_survey_extra_vars(self, mock_me): private_data_dir, extra_vars, safe_dict = call_args assert extra_vars['super_secret'] == "CLASSIFIED" + @pytest.mark.django_db def test_awx_task_env(self, patch_Job, private_data_dir, execution_environment, mock_me): job = Job(project=Project(), inventory=Inventory()) job.execution_environment = execution_environment @@ -582,7 +552,8 @@ def test_awx_task_env(self, patch_Job, private_data_dir, execution_environment, task._write_extra_vars_file = mock.Mock() with mock.patch('awx.main.tasks.jobs.settings.AWX_TASK_ENV', {'FOO': 'BAR'}): - env = task.build_env(job, private_data_dir) + with mock.patch.object(task, 'build_credentials_list', return_value=[], autospec=True): + env = task.build_env(job, private_data_dir) assert env['FOO'] == 'BAR' @@ -597,6 +568,8 @@ def test_options_jinja_usage(self, adhoc_job, adhoc_update_model_wrapper, mock_m adhoc_job.send_notification_templates = mock.Mock() task = jobs.RunAdHocCommand() + adhoc_job.status = 'running' # to bypass status flip + task.instance = adhoc_job # to bypass fetch task.update_model = mock.Mock(wraps=adhoc_update_model_wrapper) task.model.objects.get = mock.Mock(return_value=adhoc_job) task.build_inventory = mock.Mock() @@ -648,6 +621,11 @@ def test_created_by_extra_vars(self, mock_me): class TestJobCredentials(TestJobExecution): + @pytest.fixture(autouse=True) + def mock_flag_enabled(self): + with mock.patch('awx.main.tasks.jobs.flag_enabled', return_value=False): + yield + @pytest.fixture def job(self, execution_environment): job = Job(pk=1, inventory=Inventory(pk=1), project=Project(pk=1)) @@ -673,7 +651,9 @@ def _credentials_filter(credential_type__kind=None): ) with mock.patch.object(UnifiedJob, 'credentials', credentials_mock): - yield job + # Mock build_credentials_list to work with the cached credentials mechanism + with mock.patch.object(jobs.RunJob, 'build_credentials_list', return_value=job._credentials, autospec=True): + yield job @pytest.fixture def update_model_wrapper(self, job): @@ -863,202 +843,6 @@ def test_multi_vault_password_ask(self, private_data_dir, job, mock_me): assert '--vault-id dev@prompt' in ' '.join(args) assert '--vault-id prod@prompt' in ' '.join(args) - @pytest.mark.parametrize("verify", (True, False)) - def test_k8s_credential(self, job, private_data_dir, verify, mock_me): - k8s = CredentialType.defaults['kubernetes_bearer_token']() - inputs = { - 'host': 'https://example.org/', - 'bearer_token': 'token123', - } - if verify: - inputs['verify_ssl'] = True - inputs['ssl_ca_cert'] = 'CERTDATA' - credential = Credential( - pk=1, - credential_type=k8s, - inputs=inputs, - ) - credential.inputs['bearer_token'] = encrypt_field(credential, 'bearer_token') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['K8S_AUTH_HOST'] == 'https://example.org/' - assert env['K8S_AUTH_API_KEY'] == 'token123' - - if verify: - assert env['K8S_AUTH_VERIFY_SSL'] == 'True' - local_path = to_host_path(env['K8S_AUTH_SSL_CA_CERT'], private_data_dir) - cert = open(local_path, 'r').read() - assert cert == 'CERTDATA' - else: - assert env['K8S_AUTH_VERIFY_SSL'] == 'False' - assert 'K8S_AUTH_SSL_CA_CERT' not in env - - assert safe_env['K8S_AUTH_API_KEY'] == HIDDEN_PASSWORD - - def test_aws_cloud_credential(self, job, private_data_dir, mock_me): - aws = CredentialType.defaults['aws']() - credential = Credential(pk=1, credential_type=aws, inputs={'username': 'bob', 'password': 'secret'}) - credential.inputs['password'] = encrypt_field(credential, 'password') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['AWS_ACCESS_KEY_ID'] == 'bob' - assert env['AWS_SECRET_ACCESS_KEY'] == 'secret' - assert 'AWS_SECURITY_TOKEN' not in env - assert safe_env['AWS_SECRET_ACCESS_KEY'] == HIDDEN_PASSWORD - - def test_aws_cloud_credential_with_sts_token(self, private_data_dir, job, mock_me): - aws = CredentialType.defaults['aws']() - credential = Credential(pk=1, credential_type=aws, inputs={'username': 'bob', 'password': 'secret', 'security_token': 'token'}) - for key in ('password', 'security_token'): - credential.inputs[key] = encrypt_field(credential, key) - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['AWS_ACCESS_KEY_ID'] == 'bob' - assert env['AWS_SECRET_ACCESS_KEY'] == 'secret' - assert env['AWS_SECURITY_TOKEN'] == 'token' - assert safe_env['AWS_SECRET_ACCESS_KEY'] == HIDDEN_PASSWORD - - @pytest.mark.parametrize("cred_env_var", ['GCE_CREDENTIALS_FILE_PATH', 'GOOGLE_APPLICATION_CREDENTIALS']) - def test_gce_credentials(self, cred_env_var, private_data_dir, job, mock_me): - gce = CredentialType.defaults['gce']() - credential = Credential(pk=1, credential_type=gce, inputs={'username': 'bob', 'project': 'some-project', 'ssh_key_data': self.EXAMPLE_PRIVATE_KEY}) - credential.inputs['ssh_key_data'] = encrypt_field(credential, 'ssh_key_data') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - runner_path = env[cred_env_var] - local_path = to_host_path(runner_path, private_data_dir) - json_data = json.load(open(local_path, 'rb')) - assert json_data['type'] == 'service_account' - assert json_data['private_key'] == self.EXAMPLE_PRIVATE_KEY - assert json_data['client_email'] == 'bob' - assert json_data['project_id'] == 'some-project' - - def test_azure_rm_with_tenant(self, private_data_dir, job, mock_me): - azure = CredentialType.defaults['azure_rm']() - credential = Credential( - pk=1, credential_type=azure, inputs={'client': 'some-client', 'secret': 'some-secret', 'tenant': 'some-tenant', 'subscription': 'some-subscription'} - ) - credential.inputs['secret'] = encrypt_field(credential, 'secret') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['AZURE_CLIENT_ID'] == 'some-client' - assert env['AZURE_SECRET'] == 'some-secret' - assert env['AZURE_TENANT'] == 'some-tenant' - assert env['AZURE_SUBSCRIPTION_ID'] == 'some-subscription' - assert safe_env['AZURE_SECRET'] == HIDDEN_PASSWORD - - def test_azure_rm_with_password(self, private_data_dir, job, mock_me): - azure = CredentialType.defaults['azure_rm']() - credential = Credential( - pk=1, credential_type=azure, inputs={'subscription': 'some-subscription', 'username': 'bob', 'password': 'secret', 'cloud_environment': 'foobar'} - ) - credential.inputs['password'] = encrypt_field(credential, 'password') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['AZURE_SUBSCRIPTION_ID'] == 'some-subscription' - assert env['AZURE_AD_USER'] == 'bob' - assert env['AZURE_PASSWORD'] == 'secret' - assert env['AZURE_CLOUD_ENVIRONMENT'] == 'foobar' - assert safe_env['AZURE_PASSWORD'] == HIDDEN_PASSWORD - - def test_vmware_credentials(self, private_data_dir, job, mock_me): - vmware = CredentialType.defaults['vmware']() - credential = Credential(pk=1, credential_type=vmware, inputs={'username': 'bob', 'password': 'secret', 'host': 'https://example.org'}) - credential.inputs['password'] = encrypt_field(credential, 'password') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['VMWARE_USER'] == 'bob' - assert env['VMWARE_PASSWORD'] == 'secret' - assert env['VMWARE_HOST'] == 'https://example.org' - assert safe_env['VMWARE_PASSWORD'] == HIDDEN_PASSWORD - - def test_openstack_credentials(self, private_data_dir, job, mock_me): - task = jobs.RunJob() - task.instance = job - openstack = CredentialType.defaults['openstack']() - credential = Credential( - pk=1, credential_type=openstack, inputs={'username': 'bob', 'password': 'secret', 'project': 'tenant-name', 'host': 'https://keystone.example.org'} - ) - credential.inputs['password'] = encrypt_field(credential, 'password') - job.credentials.add(credential) - - private_data_files, ssh_key_data = task.build_private_data_files(job, private_data_dir) - env = task.build_env(job, private_data_dir, private_data_files=private_data_files) - credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir) - - config_loc = to_host_path(env['OS_CLIENT_CONFIG_FILE'], private_data_dir) - shade_config = open(config_loc, 'r').read() - assert shade_config == '\n'.join( - [ - 'clouds:', - ' devstack:', - ' auth:', - ' auth_url: https://keystone.example.org', - ' password: secret', - ' project_name: tenant-name', - ' username: bob', - ' verify: true', - '', - ] - ) - - @pytest.mark.parametrize("ca_file", [None, '/path/to/some/file']) - def test_rhv_credentials(self, private_data_dir, job, ca_file, mock_me): - rhv = CredentialType.defaults['rhv']() - inputs = { - 'host': 'some-ovirt-host.example.org', - 'username': 'bob', - 'password': 'some-pass', - } - if ca_file: - inputs['ca_file'] = ca_file - credential = Credential(pk=1, credential_type=rhv, inputs=inputs) - credential.inputs['password'] = encrypt_field(credential, 'password') - job.credentials.add(credential) - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - config = configparser.ConfigParser() - host_path = to_host_path(env['OVIRT_INI_PATH'], private_data_dir) - config.read(host_path) - assert config.get('ovirt', 'ovirt_url') == 'some-ovirt-host.example.org' - assert config.get('ovirt', 'ovirt_username') == 'bob' - assert config.get('ovirt', 'ovirt_password') == 'some-pass' - if ca_file: - assert config.get('ovirt', 'ovirt_ca_file') == ca_file - else: - with pytest.raises(configparser.NoOptionError): - config.get('ovirt', 'ovirt_ca_file') - @pytest.mark.parametrize( 'authorize, expected_authorize', [ @@ -1067,6 +851,7 @@ def test_rhv_credentials(self, private_data_dir, job, ca_file, mock_me): [None, '0'], ], ) + @pytest.mark.django_db def test_net_credentials(self, authorize, expected_authorize, job, private_data_dir, mock_me): task = jobs.RunJob() task.instance = job @@ -1089,259 +874,10 @@ def test_net_credentials(self, authorize, expected_authorize, job, private_data_ assert env['ANSIBLE_NET_AUTHORIZE'] == expected_authorize if authorize: assert env['ANSIBLE_NET_AUTH_PASS'] == 'authorizeme' - assert open(env['ANSIBLE_NET_SSH_KEYFILE'], 'r').read() == self.EXAMPLE_PRIVATE_KEY + with open(env['ANSIBLE_NET_SSH_KEYFILE'], 'r') as f: + assert f.read() == self.EXAMPLE_PRIVATE_KEY assert safe_env['ANSIBLE_NET_PASSWORD'] == HIDDEN_PASSWORD - def test_custom_environment_injectors_with_jinja_syntax_error(self, private_data_dir, mock_me): - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'api_token', 'label': 'API Token', 'type': 'string'}]}, - injectors={'env': {'MY_CLOUD_API_TOKEN': '{{api_token.foo()}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'api_token': 'ABC123'}) - - with pytest.raises(jinja2.exceptions.UndefinedError): - credential.credential_type.inject_credential(credential, {}, {}, [], private_data_dir) - - def test_custom_environment_injectors(self, private_data_dir, mock_me): - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'api_token', 'label': 'API Token', 'type': 'string'}]}, - injectors={'env': {'MY_CLOUD_API_TOKEN': '{{api_token}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'api_token': 'ABC123'}) - - env = {} - credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir) - - assert env['MY_CLOUD_API_TOKEN'] == 'ABC123' - - def test_custom_environment_injectors_with_boolean_env_var(self, private_data_dir, mock_me): - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'turbo_button', 'label': 'Turbo Button', 'type': 'boolean'}]}, - injectors={'env': {'TURBO_BUTTON': '{{turbo_button}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'turbo_button': True}) - - env = {} - credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir) - - assert env['TURBO_BUTTON'] == str(True) - - def test_custom_environment_injectors_with_reserved_env_var(self, private_data_dir, job, mock_me): - task = jobs.RunJob() - task.instance = job - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'api_token', 'label': 'API Token', 'type': 'string'}]}, - injectors={'env': {'JOB_ID': 'reserved'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'api_token': 'ABC123'}) - job.credentials.add(credential) - - env = task.build_env(job, private_data_dir) - - assert env['JOB_ID'] == str(job.pk) - - def test_custom_environment_injectors_with_secret_field(self, private_data_dir, mock_me): - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'password', 'label': 'Password', 'type': 'string', 'secret': True}]}, - injectors={'env': {'MY_CLOUD_PRIVATE_VAR': '{{password}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'password': 'SUPER-SECRET-123'}) - credential.inputs['password'] = encrypt_field(credential, 'password') - - env = {} - safe_env = {} - credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) - - assert env['MY_CLOUD_PRIVATE_VAR'] == 'SUPER-SECRET-123' - assert 'SUPER-SECRET-123' not in safe_env.values() - assert safe_env['MY_CLOUD_PRIVATE_VAR'] == HIDDEN_PASSWORD - - def test_custom_environment_injectors_with_extra_vars(self, private_data_dir, job, mock_me): - task = jobs.RunJob() - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'api_token', 'label': 'API Token', 'type': 'string'}]}, - injectors={'extra_vars': {'api_token': '{{api_token}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'api_token': 'ABC123'}) - job.credentials.add(credential) - - args = task.build_args(job, private_data_dir, {}) - credential.credential_type.inject_credential(credential, {}, {}, args, private_data_dir) - extra_vars = parse_extra_vars(args, private_data_dir) - - assert extra_vars["api_token"] == "ABC123" - assert hasattr(extra_vars["api_token"], '__UNSAFE__') - - def test_custom_environment_injectors_with_boolean_extra_vars(self, job, private_data_dir, mock_me): - task = jobs.RunJob() - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'turbo_button', 'label': 'Turbo Button', 'type': 'boolean'}]}, - injectors={'extra_vars': {'turbo_button': '{{turbo_button}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'turbo_button': True}) - job.credentials.add(credential) - - args = task.build_args(job, private_data_dir, {}) - credential.credential_type.inject_credential(credential, {}, {}, args, private_data_dir) - extra_vars = parse_extra_vars(args, private_data_dir) - - assert extra_vars["turbo_button"] == "True" - return ['successful', 0] - - def test_custom_environment_injectors_with_nested_extra_vars(self, private_data_dir, job, mock_me): - task = jobs.RunJob() - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'host', 'label': 'Host', 'type': 'string'}]}, - injectors={'extra_vars': {'auth': {'host': '{{host}}'}}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'host': 'example.com'}) - job.credentials.add(credential) - - args = task.build_args(job, private_data_dir, {}) - credential.credential_type.inject_credential(credential, {}, {}, args, private_data_dir) - extra_vars = parse_extra_vars(args, private_data_dir) - - assert extra_vars["auth"]["host"] == "example.com" - - def test_custom_environment_injectors_with_templated_extra_vars_key(self, private_data_dir, job, mock_me): - task = jobs.RunJob() - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'environment', 'label': 'Environment', 'type': 'string'}, {'id': 'host', 'label': 'Host', 'type': 'string'}]}, - injectors={'extra_vars': {'{{environment}}_auth': {'host': '{{host}}'}}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'environment': 'test', 'host': 'example.com'}) - job.credentials.add(credential) - - args = task.build_args(job, private_data_dir, {}) - credential.credential_type.inject_credential(credential, {}, {}, args, private_data_dir) - extra_vars = parse_extra_vars(args, private_data_dir) - - assert extra_vars["test_auth"]["host"] == "example.com" - - def test_custom_environment_injectors_with_complicated_boolean_template(self, job, private_data_dir, mock_me): - task = jobs.RunJob() - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'turbo_button', 'label': 'Turbo Button', 'type': 'boolean'}]}, - injectors={'extra_vars': {'turbo_button': '{% if turbo_button %}FAST!{% else %}SLOW!{% endif %}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'turbo_button': True}) - job.credentials.add(credential) - - args = task.build_args(job, private_data_dir, {}) - credential.credential_type.inject_credential(credential, {}, {}, args, private_data_dir) - extra_vars = parse_extra_vars(args, private_data_dir) - - assert extra_vars["turbo_button"] == "FAST!" - - def test_custom_environment_injectors_with_secret_extra_vars(self, job, private_data_dir, mock_me): - """ - extra_vars that contain secret field values should be censored in the DB - """ - task = jobs.RunJob() - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'password', 'label': 'Password', 'type': 'string', 'secret': True}]}, - injectors={'extra_vars': {'password': '{{password}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'password': 'SUPER-SECRET-123'}) - credential.inputs['password'] = encrypt_field(credential, 'password') - job.credentials.add(credential) - - args = task.build_args(job, private_data_dir, {}) - credential.credential_type.inject_credential(credential, {}, {}, args, private_data_dir) - - extra_vars = parse_extra_vars(args, private_data_dir) - assert extra_vars["password"] == "SUPER-SECRET-123" - - def test_custom_environment_injectors_with_file(self, private_data_dir, mock_me): - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'api_token', 'label': 'API Token', 'type': 'string'}]}, - injectors={'file': {'template': '[mycloud]\n{{api_token}}'}, 'env': {'MY_CLOUD_INI_FILE': '{{tower.filename}}'}}, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'api_token': 'ABC123'}) - - env = {} - credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir) - - path = to_host_path(env['MY_CLOUD_INI_FILE'], private_data_dir) - assert open(path, 'r').read() == '[mycloud]\nABC123' - - def test_custom_environment_injectors_with_unicode_content(self, private_data_dir, mock_me): - value = 'Iñtërnâtiônàlizætiøn' - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': []}, - injectors={'file': {'template': value}, 'env': {'MY_CLOUD_INI_FILE': '{{tower.filename}}'}}, - ) - credential = Credential( - pk=1, - credential_type=some_cloud, - ) - - env = {} - credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir) - - path = to_host_path(env['MY_CLOUD_INI_FILE'], private_data_dir) - assert open(path, 'r').read() == value - - def test_custom_environment_injectors_with_files(self, private_data_dir, mock_me): - some_cloud = CredentialType( - kind='cloud', - name='SomeCloud', - managed=False, - inputs={'fields': [{'id': 'cert', 'label': 'Certificate', 'type': 'string'}, {'id': 'key', 'label': 'Key', 'type': 'string'}]}, - injectors={ - 'file': {'template.cert': '[mycert]\n{{cert}}', 'template.key': '[mykey]\n{{key}}'}, - 'env': {'MY_CERT_INI_FILE': '{{tower.filename.cert}}', 'MY_KEY_INI_FILE': '{{tower.filename.key}}'}, - }, - ) - credential = Credential(pk=1, credential_type=some_cloud, inputs={'cert': 'CERT123', 'key': 'KEY123'}) - - env = {} - credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir) - - cert_path = to_host_path(env['MY_CERT_INI_FILE'], private_data_dir) - key_path = to_host_path(env['MY_KEY_INI_FILE'], private_data_dir) - assert open(cert_path, 'r').read() == '[mycert]\nCERT123' - assert open(key_path, 'r').read() == '[mykey]\nKEY123' - def test_multi_cloud(self, private_data_dir, mock_me): gce = CredentialType.defaults['gce']() gce_credential = Credential(pk=1, credential_type=gce, inputs={'username': 'bob', 'project': 'some-project', 'ssh_key_data': self.EXAMPLE_PRIVATE_KEY}) @@ -1363,7 +899,8 @@ def test_multi_cloud(self, private_data_dir, mock_me): # Because this is testing a mix of multiple cloud creds, we are not going to test the GOOGLE_APPLICATION_CREDENTIALS here path = to_host_path(env['GCE_CREDENTIALS_FILE_PATH'], private_data_dir) - json_data = json.load(open(path, 'rb')) + with open(path, 'rb') as f: + json_data = json.load(f) assert json_data['type'] == 'service_account' assert json_data['private_key'] == self.EXAMPLE_PRIVATE_KEY assert json_data['client_email'] == 'bob' @@ -1371,6 +908,7 @@ def test_multi_cloud(self, private_data_dir, mock_me): assert safe_env['AZURE_PASSWORD'] == HIDDEN_PASSWORD + @pytest.mark.django_db def test_awx_task_env(self, settings, private_data_dir, job, mock_me): settings.AWX_TASK_ENV = {'FOO': 'BAR'} task = jobs.RunJob() @@ -1556,7 +1094,76 @@ def test_awx_task_env(self, project_update, settings, private_data_dir, scm_type assert env['FOO'] == 'BAR' +@pytest.mark.django_db +class TestProjectUpdateRefspec(TestJobExecution): + @pytest.fixture + def project_update(self, execution_environment): + org = Organization(pk=1) + proj = Project(pk=1, organization=org, allow_override=True) + project_update = ProjectUpdate(pk=1, project=proj, scm_type='git') + project_update.websocket_emit_status = mock.Mock() + project_update.execution_environment = execution_environment + return project_update + + def test_refspec_with_allow_override_includes_plus_prefix(self, project_update, private_data_dir, mock_me): + """Test that refspec includes + prefix to allow non-fast-forward updates when allow_override is True""" + task = jobs.RunProjectUpdate() + task.instance = project_update + + # Call build_extra_vars_file which sets the refspec + with mock.patch.object(Licenser, 'validate', lambda *args, **kw: {}): + task.build_extra_vars_file(project_update, private_data_dir) + + # Read the extra vars file to check the refspec + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) + + # Verify the refspec includes the + prefix for force updates + assert 'scm_refspec' in extra_vars + assert extra_vars['scm_refspec'] == '+refs/heads/*:refs/remotes/origin/*' + + def test_custom_refspec_not_overridden(self, project_update, private_data_dir, mock_me): + """Test that custom user-provided refspec is not overridden""" + task = jobs.RunProjectUpdate() + task.instance = project_update + project_update.scm_refspec = 'refs/pull/*/head:refs/remotes/origin/pr/*' + + with mock.patch.object(Licenser, 'validate', lambda *args, **kw: {}): + task.build_extra_vars_file(project_update, private_data_dir) + + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) + + # Custom refspec should be preserved + assert extra_vars['scm_refspec'] == 'refs/pull/*/head:refs/remotes/origin/pr/*' + + def test_no_refspec_without_allow_override(self, execution_environment, private_data_dir, mock_me): + """Test that no refspec is set when allow_override is False""" + org = Organization(pk=1) + proj = Project(pk=1, organization=org, allow_override=False) + project_update = ProjectUpdate(pk=1, project=proj, scm_type='git') + project_update.websocket_emit_status = mock.Mock() + project_update.execution_environment = execution_environment + + task = jobs.RunProjectUpdate() + task.instance = project_update + + with mock.patch.object(Licenser, 'validate', lambda *args, **kw: {}): + task.build_extra_vars_file(project_update, private_data_dir) + + with open(os.path.join(private_data_dir, 'env', 'extravars')) as fd: + extra_vars = yaml.load(fd, Loader=SafeLoader) + + # No refspec should be set + assert 'scm_refspec' not in extra_vars + + class TestInventoryUpdateCredentials(TestJobExecution): + @pytest.fixture(autouse=True) + def mock_flag_enabled(self): + with mock.patch('awx.main.tasks.jobs.flag_enabled', return_value=False): + yield + @pytest.fixture def inventory_update(self, execution_environment): return InventoryUpdate(pk=1, execution_environment=execution_environment, inventory_source=InventorySource(pk=1, inventory=Inventory(pk=1))) @@ -1716,7 +1323,8 @@ def run(expected_gce_zone): credential.credential_type.inject_credential(credential, env, safe_env, [], private_data_dir) assert env['GCE_ZONE'] == expected_gce_zone - json_data = json.load(open(env[cred_env_var], 'rb')) + with open(env[cred_env_var], 'rb') as f: + json_data = json.load(f) assert json_data['type'] == 'service_account' assert json_data['private_key'] == self.EXAMPLE_PRIVATE_KEY assert json_data['client_email'] == 'bob' @@ -1745,7 +1353,8 @@ def get_cred(): env = task.build_env(inventory_update, private_data_dir, private_data_files) path = to_host_path(env['OS_CLIENT_CONFIG_FILE'], private_data_dir) - shade_config = open(path, 'r').read() + with open(path, 'r') as f: + shade_config = f.read() assert ( '\n'.join( [ @@ -1908,8 +1517,8 @@ def test_fcntl_ioerror(): @mock.patch('os.open') -@mock.patch('logging.getLogger') -def test_acquire_lock_open_fail_logged(logging_getLogger, os_open, mock_me): +@mock.patch('awx.main.tasks.jobs.logger') +def test_acquire_lock_open_fail_logged(logger_mock, os_open, mock_me): err = OSError() err.errno = 3 err.strerror = 'dummy message' @@ -1919,21 +1528,18 @@ def test_acquire_lock_open_fail_logged(logging_getLogger, os_open, mock_me): os_open.side_effect = err - logger = mock.Mock() - logging_getLogger.return_value = logger - ProjectUpdate = jobs.RunProjectUpdate() with pytest.raises(OSError): ProjectUpdate.acquire_lock(instance) - assert logger.err.called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message')) + logger_mock.error.assert_called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message')) @mock.patch('os.open') @mock.patch('os.close') -@mock.patch('logging.getLogger') +@mock.patch('awx.main.tasks.jobs.logger') @mock.patch('fcntl.lockf') -def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logging_getLogger, os_close, os_open, mock_me): +def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logger_mock, os_close, os_open, mock_me): err = IOError() err.errno = 3 err.strerror = 'dummy message' @@ -1944,16 +1550,15 @@ def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logging_getLogger, os os_open.return_value = 3 - logger = mock.Mock() - logging_getLogger.return_value = logger - fcntl_lockf.side_effect = err ProjectUpdate = jobs.RunProjectUpdate() with pytest.raises(IOError): ProjectUpdate.acquire_lock(instance) os_close.assert_called_with(3) - assert logger.err.called_with("I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message')) + logger_mock.error.assert_called_with( + "I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message') + ) @pytest.mark.parametrize('injector_cls', [cls for cls in ManagedCredentialType.registry.values() if cls.injectors]) @@ -1978,7 +1583,7 @@ def test_managed_injector_redaction(injector_cls): assert 'very_secret_value' not in str(build_safe_env(env)) -def test_job_run_no_ee(mock_me, mock_create_partition): +def test_job_run_no_ee(mock_me, mock_create_partition, private_data_dir): org = Organization(pk=1) proj = Project(pk=1, organization=org) job = Job(project=proj, organization=org, inventory=Inventory(pk=1)) @@ -2008,7 +1613,7 @@ def test_project_update_no_ee(mock_me): with pytest.raises(RuntimeError) as e: task.build_env(job, {}) - assert 'The project could not sync because there is no Execution Environment' in str(e.value) + assert 'The ProjectUpdate could not run because there is no Execution Environment' in str(e.value) @pytest.mark.parametrize( diff --git a/awx/main/tests/unit/test_validators.py b/awx/main/tests/unit/test_validators.py index 925ea64335eb..2512ab28af73 100644 --- a/awx/main/tests/unit/test_validators.py +++ b/awx/main/tests/unit/test_validators.py @@ -132,6 +132,25 @@ def test_cert_with_key(): assert not pem_objects[1]['key_enc'] +def test_ssh_key_with_whitespace(): + # Test that SSH keys with leading/trailing whitespace/newlines are properly sanitized + # This addresses issue #14219 where copy-paste can introduce hidden newlines + valid_key_with_whitespace = "\n\n" + TEST_SSH_KEY_DATA + "\n\n" + pem_objects = validate_ssh_private_key(valid_key_with_whitespace) + assert pem_objects[0]['key_type'] == 'rsa' + assert not pem_objects[0]['key_enc'] + + # Test with just leading whitespace + valid_key_leading = "\n\n\n" + TEST_SSH_KEY_DATA + pem_objects = validate_ssh_private_key(valid_key_leading) + assert pem_objects[0]['key_type'] == 'rsa' + + # Test with just trailing whitespace + valid_key_trailing = TEST_SSH_KEY_DATA + "\n\n\n" + pem_objects = validate_ssh_private_key(valid_key_trailing) + assert pem_objects[0]['key_type'] == 'rsa' + + @pytest.mark.parametrize( "var_str", [ diff --git a/awx/main/tests/unit/test_views.py b/awx/main/tests/unit/test_views.py index e9e2c67baf3a..371e44157ab5 100644 --- a/awx/main/tests/unit/test_views.py +++ b/awx/main/tests/unit/test_views.py @@ -10,7 +10,6 @@ from awx.api.views import JobList from awx.api.generics import ListCreateAPIView, SubListAttachDetachAPIView - HTTP_METHOD_NAMES = [ 'get', 'post', @@ -88,6 +87,6 @@ def test_global_creation_always_possible(all_views): creatable_view = View if not creatable or not global_view: continue - assert 'POST' in global_view().allowed_methods, 'Resource {} should be creatable in global list view {}. ' 'Can be created now in {}'.format( + assert 'POST' in global_view().allowed_methods, 'Resource {} should be creatable in global list view {}. Can be created now in {}'.format( model, global_view, creatable_view ) diff --git a/awx/main/tests/unit/utils/test_analytics_proxy.py b/awx/main/tests/unit/utils/test_analytics_proxy.py new file mode 100644 index 000000000000..0a49c33cb97a --- /dev/null +++ b/awx/main/tests/unit/utils/test_analytics_proxy.py @@ -0,0 +1,112 @@ +import pytest +import requests +from unittest import mock + +from awx.main.utils.analytics_proxy import OIDCClient, TokenType, TokenError + +MOCK_TOKEN_RESPONSE = { + 'access_token': 'bob-access-token', + 'expires_in': 500, + 'refresh_expires_in': 900, + 'token_type': 'Bearer', + 'not-before-policy': 6, + 'scope': 'fake-scope1, fake-scope2', +} + + +@pytest.fixture +def oidc_client(): + ''' + oidc client instantiation fixture. + ''' + return OIDCClient( + 'fake-client-id', + 'fake-client-secret', + 'https://my-token-url.com/get/a/token/', + ['api.console'], + ) + + +@pytest.fixture +def token(): + ''' + Create Token class out of example OIDC token response. + ''' + return OIDCClient._json_response_to_token(MOCK_TOKEN_RESPONSE) + + +def test_generate_access_token(oidc_client): + with mock.patch( + 'awx.main.utils.analytics_proxy.requests.post', + return_value=mock.Mock(json=lambda: MOCK_TOKEN_RESPONSE, raise_for_status=mock.Mock(return_value=None)), # No exception raised + ): + oidc_client._generate_access_token() + + assert oidc_client.token + assert oidc_client.token.access_token == 'bob-access-token' + assert oidc_client.token.expires_in == 500 + assert oidc_client.token.refresh_expires_in == 900 + assert oidc_client.token.token_type == TokenType.BEARER + assert oidc_client.token.not_before_policy == 6 + assert oidc_client.token.scope == 'fake-scope1, fake-scope2' + + +def test_token_generation_error(oidc_client): + ''' + Check that TokenError is raised for failure in token generation process + ''' + exception_404 = requests.HTTPError('404 Client Error: Not Found for url') + with mock.patch( + 'awx.main.utils.analytics_proxy.requests.post', + return_value=mock.Mock(status_code=404, json=mock.Mock(return_value={'error': 'Not Found'}), raise_for_status=mock.Mock(side_effect=exception_404)), + ): + with pytest.raises(TokenError) as exc_info: + oidc_client._generate_access_token() + + assert exc_info.value.__cause__ == exception_404 + + +def test_make_request(oidc_client, token): + ''' + Check that make_request makes an http request with a generated token. + ''' + + def fake_generate_access_token(): + oidc_client.token = token + + with ( + mock.patch.object(oidc_client, '_generate_access_token', side_effect=fake_generate_access_token), + mock.patch('awx.main.utils.analytics_proxy.requests.request') as mock_request, + ): + oidc_client.make_request('GET', 'https://does_not_exist.com') + + mock_request.assert_called_with( + 'GET', + 'https://does_not_exist.com', + headers={ + 'Authorization': f'Bearer {token.access_token}', + 'Accept': 'application/json', + }, + ) + + +def test_make_request_existing_token(oidc_client, token): + ''' + Check that make_request does not try and generate a token. + ''' + oidc_client.token = token + + with ( + mock.patch.object(oidc_client, '_generate_access_token', side_effect=RuntimeError('expected not to be called')), + mock.patch('awx.main.utils.analytics_proxy.requests.request') as mock_request, + ): + oidc_client.make_request('GET', 'https://does_not_exist.com') + + mock_request.assert_called_with( + 'GET', + 'https://does_not_exist.com', + headers={ + 'Authorization': f'Bearer {token.access_token}', + 'Accept': 'application/json', + }, + ) diff --git a/awx/main/tests/unit/utils/test_common.py b/awx/main/tests/unit/utils/test_common.py index cc8f65bf9333..b5983497b20a 100644 --- a/awx/main/tests/unit/utils/test_common.py +++ b/awx/main/tests/unit/utils/test_common.py @@ -12,6 +12,8 @@ from rest_framework.exceptions import ParseError +from ansible_base.lib.utils.models import get_type_for_model + from awx.main.utils import common from awx.api.validators import HostnameRegexValidator @@ -106,7 +108,7 @@ def test_set_environ(): # Cases relied on for scheduler dependent jobs list @pytest.mark.parametrize('model,name', TEST_MODELS) def test_get_type_for_model(model, name): - assert common.get_type_for_model(model) == name + assert get_type_for_model(model) == name def test_get_model_for_invalid_type(): @@ -119,6 +121,10 @@ def test_get_model_for_valid_type(model_type, model_class): assert common.get_model_for_type(model_type) == model_class +def test_is_testing(): + assert common.is_testing() is True + + @pytest.mark.parametrize("model_type,model_class", [(name, cls) for cls, name in TEST_MODELS]) def test_get_capacity_type(model_type, model_class): if model_type in ('job', 'ad_hoc_command', 'inventory_update', 'job_template'): @@ -234,7 +240,15 @@ def test_extract_ansible_vars(): ('git', 'https://example.com/bar.git', 'user', 'pw', True, False, 'https://user:pw@example.com/bar.git'), ('git', 'https://example@example.com/bar.git', False, 'something', True, False, 'https://example.com/bar.git'), # Special github/bitbucket cases - ('git', 'notgit@github.com:ansible/awx.git', True, True, True, False, ValueError('Username must be "git" for SSH access to github.com.')), + ( + 'git', + 'notgit@github.com:ansible/awx.git', + True, + True, + True, + False, + ValueError('Username must be "git" for SSH access to github.com.'), + ), ( 'git', 'notgit@bitbucket.org:does-not-exist/example.git', @@ -312,22 +326,18 @@ def test_hostame_regex_validator_default_constructor(self, regex_expr, re_flags) def test_good_call(self, regex_expr, re_flags): h = HostnameRegexValidator(regex=regex_expr, flags=re_flags) - assert (h("192.168.56.101"), None) + assert h("192.168.56.101") is None def test_bad_call(self, regex_expr, re_flags): h = HostnameRegexValidator(regex=regex_expr, flags=re_flags) - try: + with pytest.raises(ValidationError, match=r"^\['illegal characters detected in hostname=@#\$%\)\$#\(TUFAS_DG. Please verify.'\]$"): h("@#$%)$#(TUFAS_DG") - except ValidationError as e: - assert e.message is not None def test_good_call_with_inverse(self, regex_expr, re_flags, inverse_match=True): h = HostnameRegexValidator(regex=regex_expr, flags=re_flags, inverse_match=inverse_match) - try: + with pytest.raises(ValidationError, match=r"^\['Enter a valid value.'\]$"): h("1.2.3.4") - except ValidationError as e: - assert e.message is not None def test_bad_call_with_inverse(self, regex_expr, re_flags, inverse_match=True): h = HostnameRegexValidator(regex=regex_expr, flags=re_flags, inverse_match=inverse_match) - assert (h("@#$%)$#(TUFAS_DG"), None) + assert h("@#$%)$#(TUFAS_DG") is None diff --git a/awx/main/tests/unit/utils/test_execution_environments.py b/awx/main/tests/unit/utils/test_execution_environments.py index 04a8b05f5f72..31ed3eaf7d3b 100644 --- a/awx/main/tests/unit/utils/test_execution_environments.py +++ b/awx/main/tests/unit/utils/test_execution_environments.py @@ -1,11 +1,8 @@ -import shutil import os -from uuid import uuid4 import pytest -from awx.main.utils.execution_environments import to_container_path - +from awx_plugins.interfaces._temporary_private_container_api import get_incontainer_path private_data_dir = '/tmp/pdd_iso/awx_xxx' @@ -22,25 +19,19 @@ ], ) def test_switch_paths(container_path, host_path): - assert to_container_path(host_path, private_data_dir) == container_path - + assert get_incontainer_path(host_path, private_data_dir) == container_path -def test_symlink_isolation_dir(request): - rand_str = str(uuid4())[:8] - dst_path = f'/tmp/ee_{rand_str}_symlink_dst' - src_path = f'/tmp/ee_{rand_str}_symlink_src' - def remove_folders(): - os.unlink(dst_path) - shutil.rmtree(src_path) +def test_symlink_isolation_dir(tmp_path): + src_path = tmp_path / 'symlink_src' + dst_path = tmp_path / 'symlink_dst' - request.addfinalizer(remove_folders) - os.mkdir(src_path) + src_path.mkdir() os.symlink(src_path, dst_path) pdd = f'{dst_path}/awx_xxx' - assert to_container_path(f'{pdd}/env/tmp1234', pdd) == '/runner/env/tmp1234' + assert get_incontainer_path(f'{pdd}/env/tmp1234', pdd) == '/runner/env/tmp1234' @pytest.mark.parametrize( @@ -53,4 +44,4 @@ def remove_folders(): ) def test_invalid_host_path(host_path): with pytest.raises(RuntimeError): - to_container_path(host_path, private_data_dir) + get_incontainer_path(host_path, private_data_dir) diff --git a/awx/main/tests/unit/utils/test_filters.py b/awx/main/tests/unit/utils/test_filters.py index ef0abb80d3f1..c2b23de0eea0 100644 --- a/awx/main/tests/unit/utils/test_filters.py +++ b/awx/main/tests/unit/utils/test_filters.py @@ -68,7 +68,9 @@ def __init__(self): @mock.patch('awx.main.utils.filters.get_model', return_value=mockHost()) class TestSmartFilterQueryFromString: - @mock.patch('awx.api.filters.get_fields_from_path', lambda model, path: ([model], path)) # disable field filtering, because a__b isn't a real Host field + @mock.patch( + 'ansible_base.rest_filters.rest_framework.field_lookup_backend.get_fields_from_path', lambda model, path, **kwargs: ([model], path) + ) # disable field filtering, because a__b isn't a real Host field @pytest.mark.parametrize( "filter_string,q_expected", [ @@ -93,7 +95,7 @@ def test_query_generated(self, mock_get_host_model, filter_string, q_expected): @pytest.mark.parametrize( "filter_string", [ - 'ansible_facts__facts__facts__blank=' 'ansible_facts__a__b__c__ space =ggg', + 'ansible_facts__facts__facts__blank=ansible_facts__a__b__c__ space =ggg', ], ) def test_invalid_filter_strings(self, mock_get_host_model, filter_string): @@ -104,7 +106,7 @@ def test_invalid_filter_strings(self, mock_get_host_model, filter_string): @pytest.mark.parametrize( "filter_string", [ - 'created_by__password__icontains=pbkdf2' 'search=foo or created_by__password__icontains=pbkdf2', + 'created_by__password__icontains=pbkdf2search=foo or created_by__password__icontains=pbkdf2', 'created_by__password__icontains=pbkdf2 or search=foo', ], ) @@ -116,8 +118,8 @@ def test_forbidden_filter_string(self, mock_get_host_model, filter_string): @pytest.mark.parametrize( "filter_string,q_expected", [ - (u'(a=abc\u1F5E3def)', Q(**{u"a": u"abc\u1F5E3def"})), - (u'(ansible_facts__a=abc\u1F5E3def)', Q(**{u"ansible_facts__contains": {u"a": u"abc\u1F5E3def"}})), + (u'(a=abc\u1f5e3def)', Q(**{u"a": u"abc\u1f5e3def"})), + (u'(ansible_facts__a=abc\u1f5e3def)', Q(**{u"ansible_facts__contains": {u"a": u"abc\u1f5e3def"}})), ], ) def test_unicode(self, mock_get_host_model, filter_string, q_expected): diff --git a/awx/main/tests/unit/utils/test_inventory_vars.py b/awx/main/tests/unit/utils/test_inventory_vars.py new file mode 100644 index 000000000000..8ba55c900e17 --- /dev/null +++ b/awx/main/tests/unit/utils/test_inventory_vars.py @@ -0,0 +1,110 @@ +""" +Test utility functions and classes for inventory variable handling. +""" + +import pytest + +from awx.main.utils.inventory_vars import InventoryVariable +from awx.main.utils.inventory_vars import InventoryGroupVariables + + +def test_inventory_variable_update_basic(): + """Test basic functionality of an inventory variable.""" + x = InventoryVariable("x") + assert x.has_no_source + x.update(1, 101) + assert str(x) == "1" + x.update(2, 102) + assert str(x) == "2" + x.update(3, 103) + assert str(x) == "3" + x.delete(102) + assert str(x) == "3" + x.delete(103) + assert str(x) == "1" + x.delete(101) + assert x.value is None + assert x.has_no_source + + +@pytest.mark.parametrize( + "updates", # (, , ) + [ + ((101, 1, 1),), + ((101, 1, 1), (101, None, None)), + ((101, 1, 1), (102, 2, 2), (102, None, 1)), + ((101, 1, 1), (102, 2, 2), (101, None, 2), (102, None, None)), + ( + (101, 0, 0), + (101, 1, 1), + (102, 2, 2), + (103, 3, 3), + (102, None, 3), + (103, None, 1), + (101, None, None), + ), + ], +) +def test_inventory_variable_update(updates: tuple[int, int | None, int | None]): + """ + Test if the variable value is set correctly on a sequence of updates. + + For this test, the value `None` implies the deletion of the source. + """ + x = InventoryVariable("x") + for src_id, value, expected_value in updates: + if value is None: + x.delete(src_id) + else: + x.update(value, src_id) + assert x.value == expected_value + + +def test_inventory_group_variables_update_basic(): + """Test basic functionality of an inventory variables update.""" + vars = InventoryGroupVariables(1) + vars.update_from_src({"x": 1, "y": 2}, 101) + assert vars == {"x": 1, "y": 2} + + +@pytest.mark.parametrize( + "updates", # (, : dict, : dict) + [ + ((101, {"x": 1, "y": 1}, {"x": 1, "y": 1}),), + ( + (101, {"x": 1, "y": 1}, {"x": 1, "y": 1}), + (102, {}, {"x": 1, "y": 1}), + ), + ( + (101, {"x": 1, "y": 1}, {"x": 1, "y": 1}), + (102, {"x": 2}, {"x": 2, "y": 1}), + ), + ( + (101, {"x": 1, "y": 1}, {"x": 1, "y": 1}), + (102, {"x": 2, "y": 2}, {"x": 2, "y": 2}), + ), + ( + (101, {"x": 1, "y": 1}, {"x": 1, "y": 1}), + (102, {"x": 2, "z": 2}, {"x": 2, "y": 1, "z": 2}), + ), + ( + (101, {"x": 1, "y": 1}, {"x": 1, "y": 1}), + (102, {"x": 2, "z": 2}, {"x": 2, "y": 1, "z": 2}), + (102, {}, {"x": 1, "y": 1}), + ), + ( + (101, {"x": 1, "y": 1}, {"x": 1, "y": 1}), + (102, {"x": 2, "z": 2}, {"x": 2, "y": 1, "z": 2}), + (103, {"x": 3}, {"x": 3, "y": 1, "z": 2}), + (101, {}, {"x": 3, "z": 2}), + ), + ], +) +def test_inventory_group_variables_update(updates: tuple[int, int | None, int | None]): + """ + Test if the group vars are set correctly on various update sequences. + """ + groupvars = InventoryGroupVariables(2) + for src_id, vars, expected_vars in updates: + groupvars.update_from_src(vars, src_id) + assert groupvars == expected_vars diff --git a/awx/main/tests/unit/utils/test_receptor.py b/awx/main/tests/unit/utils/test_receptor.py index 0a7e182070c7..123044fcad1b 100644 --- a/awx/main/tests/unit/utils/test_receptor.py +++ b/awx/main/tests/unit/utils/test_receptor.py @@ -3,7 +3,7 @@ def test_file_cleanup_scenario(): args = _convert_args_to_cli({'exclude_strings': ['awx_423_', 'awx_582_'], 'file_pattern': '/tmp/awx_*_*'}) - assert ' '.join(args) == 'cleanup --exclude-strings=awx_423_ awx_582_ --file-pattern=/tmp/awx_*_*' + assert ' '.join(args) == 'cleanup --exclude-strings "awx_423_" "awx_582_" --file-pattern=/tmp/awx_*_*' def test_image_cleanup_scenario(): @@ -17,5 +17,6 @@ def test_image_cleanup_scenario(): } ) assert ( - ' '.join(args) == 'cleanup --remove-images=quay.invalid/foo/bar:latest quay.invalid/foo/bar:devel --image-prune --process-isolation-executable=podman' + ' '.join(args) + == 'cleanup --remove-images "quay.invalid/foo/bar:latest" "quay.invalid/foo/bar:devel" --image-prune --process-isolation-executable=podman' ) diff --git a/awx/main/tests/unit/utils/test_redis.py b/awx/main/tests/unit/utils/test_redis.py new file mode 100644 index 000000000000..23e0940fc032 --- /dev/null +++ b/awx/main/tests/unit/utils/test_redis.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Ansible, Inc. +# All Rights Reserved + +from django.test.utils import override_settings + +from awx.main.utils.redis import get_redis_client, get_redis_client_async +from redis.exceptions import BusyLoadingError, ConnectionError, TimeoutError +from redis.backoff import ExponentialBackoff + + +class TestRedisRetryConfiguration: + """Verify Redis retry configuration is applied to connection objects.""" + + def test_retry_configuration_applied_to_client(self, settings): + """Verify all retry settings are applied to the connection pool.""" + # Test sync client + client = get_redis_client() + retry = client.connection_pool.connection_kwargs['retry'] + backoff = retry._backoff + retry_errors = client.connection_pool.connection_kwargs['retry_on_error'] + + # Assert provided values match values on the object + assert retry._retries == settings.REDIS_RETRY_COUNT == 3 + assert isinstance(backoff, ExponentialBackoff) + assert backoff._base == settings.REDIS_BACKOFF_BASE == 0.5 + assert backoff._cap == settings.REDIS_BACKOFF_CAP == 1.0 + assert BusyLoadingError in retry_errors + assert ConnectionError in retry_errors + assert TimeoutError in retry_errors + + # Test async client has same config + client_async = get_redis_client_async() + retry_async = client_async.connection_pool.connection_kwargs['retry'] + backoff_async = retry_async._backoff + retry_errors_async = client_async.connection_pool.connection_kwargs['retry_on_error'] + + assert retry_async._retries == settings.REDIS_RETRY_COUNT + assert backoff_async._base == settings.REDIS_BACKOFF_BASE + assert backoff_async._cap == settings.REDIS_BACKOFF_CAP + assert ConnectionError in retry_errors_async + + @override_settings(REDIS_RETRY_COUNT=5) + def test_override_settings_applied_to_client(self): + """Verify override_settings changes are applied to client object.""" + client = get_redis_client() + retry = client.connection_pool.connection_kwargs['retry'] + + assert retry._retries == 5 + + @override_settings(REDIS_BACKOFF_CAP=2.0, REDIS_BACKOFF_BASE=1.0) + def test_override_backoff_settings_applied_to_client(self): + """Verify override_settings for backoff parameters are applied to client object.""" + client = get_redis_client() + retry = client.connection_pool.connection_kwargs['retry'] + backoff = retry._backoff + + # Assert provided values match values on object + assert backoff._cap == 2.0 + assert backoff._base == 1.0 diff --git a/awx/main/tests/unit/utils/test_reload.py b/awx/main/tests/unit/utils/test_reload.py index 5f8c7b95e35b..2b41a5fef0c1 100644 --- a/awx/main/tests/unit/utils/test_reload.py +++ b/awx/main/tests/unit/utils/test_reload.py @@ -7,15 +7,15 @@ def test_produce_supervisor_command(mocker): mock_process = mocker.MagicMock() mock_process.communicate = communicate_mock Popen_mock = mocker.MagicMock(return_value=mock_process) - with mocker.patch.object(reload.subprocess, 'Popen', Popen_mock): - reload.supervisor_service_command("restart") - reload.subprocess.Popen.assert_called_once_with( - [ - 'supervisorctl', - 'restart', - 'tower-processes:*', - ], - stderr=-1, - stdin=-1, - stdout=-1, - ) + mocker.patch.object(reload.subprocess, 'Popen', Popen_mock) + reload.supervisor_service_command("restart") + reload.subprocess.Popen.assert_called_once_with( + [ + 'supervisorctl', + 'restart', + 'tower-processes:*', + ], + stderr=-1, + stdin=-1, + stdout=-1, + ) diff --git a/awx/main/tests/unit/utils/test_schedule_fast_forward.py b/awx/main/tests/unit/utils/test_schedule_fast_forward.py new file mode 100644 index 000000000000..be1bdae53eff --- /dev/null +++ b/awx/main/tests/unit/utils/test_schedule_fast_forward.py @@ -0,0 +1,176 @@ +import pytest +import datetime +import dateutil + +from django.utils.timezone import now + +from awx.main.models.schedules import _fast_forward_rrule, Schedule +from dateutil.rrule import HOURLY, MINUTELY, MONTHLY + +REF_DT = datetime.datetime(2024, 1, 1, tzinfo=datetime.timezone.utc) + + +@pytest.mark.parametrize( + 'rrulestr', + [ + pytest.param('DTSTART;TZID=America/New_York:20201118T200000 RRULE:FREQ=MINUTELY;INTERVAL=5', id='every-5-min'), + pytest.param('DTSTART;TZID=America/New_York:20201118T200000 RRULE:FREQ=HOURLY;INTERVAL=5', id='every-5-hours'), + pytest.param('DTSTART;TZID=America/New_York:20201118T200000 RRULE:FREQ=YEARLY;INTERVAL=5', id='every-5-years'), + pytest.param( + 'DTSTART;TZID=America/New_York:20201118T200000 RRULE:FREQ=MINUTELY;INTERVAL=5;WKST=SU;BYMONTH=2,3;BYMONTHDAY=18;BYHOUR=5;BYMINUTE=35;BYSECOND=0', + id='every-5-minutes-at-5:35:00-am-on-the-18th-day-of-feb-or-march-with-week-starting-on-sundays', + ), + pytest.param( + 'DTSTART;TZID=America/New_York:20201118T200000 RRULE:FREQ=HOURLY;INTERVAL=5;WKST=SU;BYMONTH=2,3;BYHOUR=5', + id='every-5-hours-at-5-am-in-feb-or-march-with-week-starting-on-sundays', + ), + ], +) +def test_fast_forwarded_rrule_matches_original_occurrence(rrulestr): + ''' + Assert that the resulting fast forwarded date is included in the original rrule + occurrence list + ''' + rruleset = Schedule.rrulestr(rrulestr, ref_dt=REF_DT) + + gen = rruleset.xafter(REF_DT, count=200) + occurrences = [i for i in gen] + + orig_rruleset = dateutil.rrule.rrulestr(rrulestr, forceset=True) + gen = orig_rruleset.xafter(REF_DT, count=200) + orig_occurrences = [i for i in gen] + + assert occurrences == orig_occurrences + + +@pytest.mark.parametrize( + 'ref_dt', + [ + pytest.param(datetime.datetime(2024, 12, 1, 0, 0, tzinfo=datetime.timezone.utc), id='ref-dt-out-of-dst'), + pytest.param(datetime.datetime(2024, 6, 1, 0, 0, tzinfo=datetime.timezone.utc), id='ref-dt-in-dst'), + ], +) +@pytest.mark.parametrize( + 'rrulestr', + [ + pytest.param('DTSTART;TZID=America/New_York:20240118T200000 RRULE:FREQ=MINUTELY;INTERVAL=10', id='rrule-out-of-dst'), + pytest.param('DTSTART;TZID=America/New_York:20240318T000000 RRULE:FREQ=MINUTELY;INTERVAL=10', id='rrule-in-dst'), + pytest.param( + 'DTSTART;TZID=Europe/Lisbon:20230703T005800 RRULE:INTERVAL=10;FREQ=MINUTELY;BYHOUR=9,10,11,12,13,14,15,16,17,18,19,20,21', id='rrule-in-dst-by-hour' + ), + ], +) +def test_fast_forward_across_dst(rrulestr, ref_dt): + ''' + Ensure fast forward works across daylight savings boundaries + "in dst" means between March and November + "out of dst" means between November and March the following year + + Assert that the resulting fast forwarded date is included in the original rrule + occurrence list + ''' + rruleset = Schedule.rrulestr(rrulestr, ref_dt=ref_dt) + + gen = rruleset.xafter(ref_dt, count=200) + occurrences = [i for i in gen] + + orig_rruleset = dateutil.rrule.rrulestr(rrulestr, forceset=True) + gen = orig_rruleset.xafter(ref_dt, count=200) + orig_occurrences = [i for i in gen] + + assert occurrences == orig_occurrences + + +def test_fast_forward_rrule_hours(): + ''' + Generate an rrule for each hour of the day + + Assert that the resulting fast forwarded date is included in the original rrule + occurrence list + ''' + rrulestr_prefix = 'DTSTART;TZID=America/New_York:20201118T200000 RRULE:FREQ=HOURLY;' + for interval in range(1, 24): + rrulestr = f"{rrulestr_prefix}INTERVAL={interval}" + rruleset = Schedule.rrulestr(rrulestr, ref_dt=REF_DT) + + gen = rruleset.xafter(REF_DT, count=200) + occurrences = [i for i in gen] + + orig_rruleset = dateutil.rrule.rrulestr(rrulestr, forceset=True) + gen = orig_rruleset.xafter(REF_DT, count=200) + orig_occurrences = [i for i in gen] + + assert occurrences == orig_occurrences + + +def test_multiple_rrules(): + ''' + Create an rruleset that contains multiple rrules and an exrule + rruleA: freq HOURLY interval 5, dtstart should be fast forwarded + rruleB: freq HOURLY interval 7, dtstart should be fast forwarded + rruleC: freq MONTHLY interval 1, dtstart should not be fast forwarded + exruleA: freq HOURLY interval 5, dtstart should be fast forwarded + ''' + rrulestr = '''DTSTART;TZID=America/New_York:20201118T200000 + RRULE:FREQ=HOURLY;INTERVAL=5 + RRULE:FREQ=HOURLY;INTERVAL=7 + RRULE:FREQ=MONTHLY + EXRULE:FREQ=HOURLY;INTERVAL=5;BYDAY=MO,TU,WE''' + rruleset = Schedule.rrulestr(rrulestr, ref_dt=REF_DT) + + rruleA, rruleB, rruleC = rruleset._rrule + exruleA = rruleset._exrule[0] + + # assert that each rrule has its own dtstart + assert rruleA._dtstart != rruleB._dtstart + assert rruleA._dtstart != rruleC._dtstart + + assert exruleA._dtstart == rruleA._dtstart + + # the new dtstart should be within INTERVAL amount of hours from REF_DT + assert (REF_DT - rruleA._dtstart) < datetime.timedelta(hours=6) + assert (REF_DT - rruleB._dtstart) < datetime.timedelta(hours=8) + assert (REF_DT - exruleA._dtstart) < datetime.timedelta(hours=6) + + # the freq=monthly rrule's dtstart should not have changed + dateutil_rruleset = dateutil.rrule.rrulestr(rrulestr, forceset=True) + assert rruleC._dtstart == dateutil_rruleset._rrule[2]._dtstart + + gen = rruleset.xafter(REF_DT, count=200) + occurrences = [i for i in gen] + + orig_rruleset = dateutil.rrule.rrulestr(rrulestr, forceset=True) + gen = orig_rruleset.xafter(REF_DT, count=200) + orig_occurrences = [i for i in gen] + + assert occurrences == orig_occurrences + + +def test_future_date_does_not_fast_forward(): + dtstart = now() + datetime.timedelta(days=30) + rrule = dateutil.rrule.rrule(freq=HOURLY, interval=7, dtstart=dtstart) + new_rrule = _fast_forward_rrule(rrule, ref_dt=REF_DT) + assert new_rrule == rrule + + +def test_rrule_with_count_does_not_fast_forward(): + rrule = dateutil.rrule.rrule(freq=MINUTELY, interval=5, count=1, dtstart=REF_DT) + + assert rrule == _fast_forward_rrule(rrule, ref_dt=REF_DT) + + +@pytest.mark.parametrize( + ('freq', 'interval'), + [ + pytest.param(MINUTELY, 15.5555, id="freq-MINUTELY-interval-15.5555"), + pytest.param(MONTHLY, 1, id="freq-MONTHLY-interval-1"), + ], +) +def test_does_not_fast_forward(freq, interval): + ''' + Assert a couple of rrules that should not be fast forwarded + ''' + dtstart = REF_DT - datetime.timedelta(days=30) + rrule = dateutil.rrule.rrule(freq=freq, interval=interval, dtstart=dtstart) + + assert rrule == _fast_forward_rrule(rrule, ref_dt=REF_DT) diff --git a/awx/main/tests/unit/utils/test_validate_rh.py b/awx/main/tests/unit/utils/test_validate_rh.py new file mode 100644 index 000000000000..65052bbdefff --- /dev/null +++ b/awx/main/tests/unit/utils/test_validate_rh.py @@ -0,0 +1,154 @@ +from unittest.mock import patch +from awx.main.utils.licensing import Licenser + + +def test_validate_rh_basic_auth_rhsm(): + """ + Assert get_rhsm_subs is called when + - basic_auth=True + - host is subscription.rhsm.redhat.com + """ + licenser = Licenser() + + with patch.object(licenser, 'get_host_from_rhsm_config', return_value='https://subscription.rhsm.redhat.com') as mock_get_host, patch.object( + licenser, 'get_rhsm_subs', return_value=[] + ) as mock_get_rhsm, patch.object(licenser, 'get_satellite_subs') as mock_get_satellite, patch.object( + licenser, 'get_crc_subs' + ) as mock_get_crc, patch.object( + licenser, 'generate_license_options_from_entitlements' + ) as mock_generate: + + licenser.validate_rh('testuser', 'testpass', basic_auth=True) + + # Assert the correct methods were called + mock_get_host.assert_called_once() + mock_get_rhsm.assert_called_once_with('https://subscription.rhsm.redhat.com', 'testuser', 'testpass') + mock_get_satellite.assert_not_called() + mock_get_crc.assert_not_called() + mock_generate.assert_called_once_with([], is_candlepin=True) + + +def test_validate_rh_basic_auth_satellite(): + """ + Assert get_satellite_subs is called when + - basic_auth=True + - custom satellite host + """ + licenser = Licenser() + + with patch.object(licenser, 'get_host_from_rhsm_config', return_value='https://satellite.example.com') as mock_get_host, patch.object( + licenser, 'get_rhsm_subs' + ) as mock_get_rhsm, patch.object(licenser, 'get_satellite_subs', return_value=[]) as mock_get_satellite, patch.object( + licenser, 'get_crc_subs' + ) as mock_get_crc, patch.object( + licenser, 'generate_license_options_from_entitlements' + ) as mock_generate: + + licenser.validate_rh('testuser', 'testpass', basic_auth=True) + + # Assert the correct methods were called + mock_get_host.assert_called_once() + mock_get_rhsm.assert_not_called() + mock_get_satellite.assert_called_once_with('https://satellite.example.com', 'testuser', 'testpass') + mock_get_crc.assert_not_called() + mock_generate.assert_called_once_with([], is_candlepin=True) + + +def test_validate_rh_service_account_crc(): + """ + Assert get_crc_subs is called when + - basic_auth=False + """ + licenser = Licenser() + + with patch('awx.main.utils.licensing.settings') as mock_settings, patch.object(licenser, 'get_host_from_rhsm_config') as mock_get_host, patch.object( + licenser, 'get_rhsm_subs' + ) as mock_get_rhsm, patch.object(licenser, 'get_satellite_subs') as mock_get_satellite, patch.object( + licenser, 'get_crc_subs', return_value=[] + ) as mock_get_crc, patch.object( + licenser, 'generate_license_options_from_entitlements' + ) as mock_generate: + + mock_settings.SUBSCRIPTIONS_RHSM_URL = 'https://console.redhat.com/api/rhsm/v1/subscriptions' + + licenser.validate_rh('client_id', 'client_secret', basic_auth=False) + + # Assert the correct methods were called + mock_get_host.assert_not_called() + mock_get_rhsm.assert_not_called() + mock_get_satellite.assert_not_called() + mock_get_crc.assert_called_once_with('https://console.redhat.com/api/rhsm/v1/subscriptions', 'client_id', 'client_secret') + mock_generate.assert_called_once_with([], is_candlepin=False) + + +def test_validate_rh_missing_user_raises_error(): + """Test validate_rh raises ValueError when user is missing""" + licenser = Licenser() + + with patch.object(licenser, 'get_host_from_rhsm_config', return_value='https://subscription.rhsm.redhat.com'): + try: + licenser.validate_rh(None, 'testpass', basic_auth=True) + assert False, "Expected ValueError to be raised" + except ValueError as e: + assert 'subscriptions_client_id or subscriptions_username is required' in str(e) + + +def test_validate_rh_missing_password_raises_error(): + """Test validate_rh raises ValueError when password is missing""" + licenser = Licenser() + + with patch.object(licenser, 'get_host_from_rhsm_config', return_value='https://subscription.rhsm.redhat.com'): + try: + licenser.validate_rh('testuser', None, basic_auth=True) + assert False, "Expected ValueError to be raised" + except ValueError as e: + assert 'subscriptions_client_secret or subscriptions_password is required' in str(e) + + +def test_validate_rh_no_host_fallback_to_candlepin(): + """Test validate_rh falls back to REDHAT_CANDLEPIN_HOST when no host from config + - basic_auth=True + - no host from config + - REDHAT_CANDLEPIN_HOST is set + """ + licenser = Licenser() + + with patch('awx.main.utils.licensing.settings') as mock_settings, patch.object( + licenser, 'get_host_from_rhsm_config', return_value=None + ) as mock_get_host, patch.object(licenser, 'get_rhsm_subs', return_value=[]) as mock_get_rhsm, patch.object( + licenser, 'get_satellite_subs', return_value=[] + ) as mock_get_satellite, patch.object( + licenser, 'get_crc_subs' + ) as mock_get_crc, patch.object( + licenser, 'generate_license_options_from_entitlements' + ) as mock_generate: + + mock_settings.REDHAT_CANDLEPIN_HOST = 'https://candlepin.example.com' + licenser.validate_rh('testuser', 'testpass', basic_auth=True) + + # Assert the correct methods were called + mock_get_host.assert_called_once() + mock_get_rhsm.assert_not_called() + mock_get_satellite.assert_called_once_with('https://candlepin.example.com', 'testuser', 'testpass') + mock_get_crc.assert_not_called() + mock_generate.assert_called_once_with([], is_candlepin=True) + + +def test_validate_rh_empty_credentials_basic_auth(): + """Test validate_rh with empty string credentials raises ValueError""" + licenser = Licenser() + + with patch.object(licenser, 'get_host_from_rhsm_config', return_value='https://subscription.rhsm.redhat.com'): + # Test empty user + try: + licenser.validate_rh(None, 'testpass', basic_auth=True) + assert False, "Expected ValueError to be raised" + except ValueError as e: + assert 'subscriptions_client_id or subscriptions_username is required' in str(e) + + # Test empty password + try: + licenser.validate_rh('testuser', None, basic_auth=True) + assert False, "Expected ValueError to be raised" + except ValueError as e: + assert 'subscriptions_client_secret or subscriptions_password is required' in str(e) diff --git a/awx/main/utils/__init__.py b/awx/main/utils/__init__.py index 2ffec9d8b617..af7473b0bd9e 100644 --- a/awx/main/utils/__init__.py +++ b/awx/main/utils/__init__.py @@ -3,6 +3,7 @@ # AWX from awx.main.utils.common import * # noqa +from awx.main.utils.redis import get_redis_client, get_redis_client_async # noqa from awx.main.utils.encryption import ( # noqa get_encryption_key, encrypt_field, diff --git a/awx/main/utils/analytics_proxy.py b/awx/main/utils/analytics_proxy.py new file mode 100644 index 000000000000..a6a599942e21 --- /dev/null +++ b/awx/main/utils/analytics_proxy.py @@ -0,0 +1,188 @@ +''' +Proxy requests Analytics requests +''' + +import time + +from enum import Enum + +from typing import Optional, Any + +import requests + +DEFAULT_OIDC_TOKEN_ENDPOINT = 'https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token' + + +class TokenError(requests.RequestException): + ''' + Raised when token generation request fails. + + Useful for differentiating request failure for make_request() vs. + other requests issued to get a token i.e.: + + try: + client = OIDCClient(...) + client.make_request(...) + except TokenError as e: + print(f"Token generation failed due to {e.__cause__}") + except requests.RequestException: + print("API request failed) + ''' + + def __init__(self, message="Token generation request failed", response=None): + super().__init__(message) + self.response = response # Store the response for debugging + + +def _now(reason: str): + ''' + Wrapper for time. Helps with testing. + ''' + return int(time.time()) + + +class TokenType(Enum): + ''' + Access token type as returned by the remote API. + ''' + + BEARER = 'Bearer' + + +class Token: + ''' + Token data generated by OIDC response. + ''' + + access_token: str + expires_in: int + refresh_expires_in: int + token_type: TokenType + not_before_policy: int # not-before-policy + scope: str + + def __init__( + self, + access_token: str, + expires_in: int, + refresh_expires_in: int, + token_type: TokenType, + not_before_policy: int, + scope: str, + ): + self.access_token = access_token + self.expires_in = expires_in + self.refresh_expires_in = refresh_expires_in + self.token_type = token_type + self.not_before_policy = not_before_policy + self.scope = scope + + self._now = _now(reason='token-creation') + + @property + def expires_at(self) -> int: + ''' + Unix timestamp in seconds of when the token expires. + ''' + return self._now + self.expires_in + + def is_expired(self) -> bool: + ''' + Check if the token is expired. + ''' + return _now(reason='token-expiration-check') >= self.expires_at + + +class OIDCClient: + ''' + Wraps requests library make_request() and manages OIDC access token. + ''' + + def __init__( + self, + client_id: str, + client_secret: str, + token_url: str = DEFAULT_OIDC_TOKEN_ENDPOINT, + scopes: list[str] = None, + base_url: str = '', + ) -> None: + self.client_id: str = client_id + self.client_secret: str = client_secret + self.token_url: str = token_url + if scopes is None: + scopes = ['api.console'] + self.scopes = scopes + self.base_url: str = base_url + self.token: Optional[Token] = None + + @classmethod + def _json_response_to_token(cls, json_response: Any) -> Token: + return Token( + access_token=json_response['access_token'], + expires_in=json_response['expires_in'], + refresh_expires_in=json_response['refresh_expires_in'], + token_type=TokenType(json_response['token_type']), + not_before_policy=json_response['not-before-policy'], + scope=json_response['scope'], + ) + + def _generate_access_token(self) -> None: + ''' + Fetches the initial access token using client credentials. + ''' + response = requests.post( + self.token_url, + data={ + 'grant_type': 'client_credentials', + 'client_id': self.client_id, + 'client_secret': self.client_secret, + 'scope': self.scopes, + }, + headers={'Content-Type': 'application/x-www-form-urlencoded'}, + ) + try: + response.raise_for_status() + except requests.RequestException as e: + raise TokenError() from e + self.token = OIDCClient._json_response_to_token(response.json()) + + def _add_headers(self, headers: dict[str, str]) -> None: + ''' + Add token header + ''' + headers.update( + { + 'Authorization': f'Bearer {self.token.access_token}', + 'Accept': 'application/json', + } + ) + + def _make_request(self, method: str, url: str, headers: dict[str, str], **kwargs: Any) -> requests.Response: + ''' + Actually make an API call. + ''' + self._add_headers(headers) + return requests.request(method, url, headers=headers, **kwargs) + + def make_request(self, method: str, endpoint: str, **kwargs: Any) -> requests.Response: + ''' + Makes an authenticated request and refreshes the token if expired. + ''' + has_generated_token = False + + def generate_access_token(): + self._generate_access_token() + return True + + if not self.token or self.token.is_expired(): + has_generated_token = generate_access_token() + + url = f'{self.base_url}{endpoint}' + headers = kwargs.pop('headers', {}) + + response = self._make_request(method, url, headers, **kwargs) + if not has_generated_token and response.status_code == 401: + generate_access_token() + response = self._make_request(method, url, headers, **kwargs) + + return response diff --git a/awx/main/utils/ansible.py b/awx/main/utils/ansible.py index 64530c53007c..cd99b347de8d 100644 --- a/awx/main/utils/ansible.py +++ b/awx/main/utils/ansible.py @@ -48,15 +48,16 @@ def could_be_playbook(project_path, dir_path, filename): # show up. matched = False try: - for n, line in enumerate(codecs.open(playbook_path, 'r', encoding='utf-8', errors='ignore')): - if valid_playbook_re.match(line): - matched = True - break - # Any YAML file can also be encrypted with vault; - # allow these to be used as the main playbook. - elif n == 0 and line.startswith('$ANSIBLE_VAULT;'): - matched = True - break + with codecs.open(playbook_path, 'r', encoding='utf-8', errors='ignore') as f: + for n, line in enumerate(f): + if valid_playbook_re.match(line): + matched = True + break + # Any YAML file can also be encrypted with vault; + # allow these to be used as the main playbook. + elif n == 0 and line.startswith('$ANSIBLE_VAULT;'): + matched = True + break except IOError: return None if not matched: diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index dedd02f9957b..a02daef16631 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -6,7 +6,7 @@ import json import yaml import logging -import time +import psycopg import os import subprocess import re @@ -17,13 +17,15 @@ import contextlib import tempfile import functools +from importlib.metadata import version as _get_version +from importlib.metadata import entry_points, EntryPoint # Django from django.core.exceptions import ObjectDoesNotExist, FieldDoesNotExist from django.utils.dateparse import parse_datetime from django.utils.translation import gettext_lazy as _ from django.utils.functional import cached_property -from django.db import connection, transaction, ProgrammingError +from django.db import connection, DatabaseError, transaction, ProgrammingError, IntegrityError from django.db.models.fields.related import ForeignObjectRel, ManyToManyField from django.db.models.fields.related_descriptors import ForwardManyToOneDescriptor, ManyToManyDescriptor from django.db.models.query import QuerySet @@ -41,6 +43,9 @@ # AWX from awx.conf.license import get_license +# ansible-runner +from ansible_runner.utils.capacity import get_mem_in_bytes, get_cpu_count + logger = logging.getLogger('awx.main.utils') __all__ = [ @@ -52,12 +57,10 @@ 'get_awx_http_client_headers', 'get_awx_version', 'update_scm_url', - 'get_type_for_model', 'get_model_for_type', 'copy_model_by_class', 'copy_m2m_relationships', 'prefetch_page_capabilities', - 'to_python_boolean', 'datetime_hook', 'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', @@ -80,7 +83,6 @@ 'set_environ', 'IllegalArgumentError', 'get_custom_venv_choices', - 'get_external_account', 'ScheduleTaskManager', 'ScheduleDependencyManager', 'ScheduleWorkflowManager', @@ -90,7 +92,7 @@ 'deepmerge', 'get_event_partition_epoch', 'cleanup_new_process', - 'log_excess_runtime', + 'unified_job_class_to_event_table_name', ] @@ -110,18 +112,6 @@ def get_object_or_400(klass, *args, **kwargs): raise ParseError(*e.args) -def to_python_boolean(value, allow_none=False): - value = str(value) - if value.lower() in ('true', '1', 't'): - return True - elif value.lower() in ('false', '0', 'f'): - return False - elif allow_none and value.lower() in ('none', 'null'): - return None - else: - raise ValueError(_(u'Unable to convert "%s" to boolean') % value) - - def datetime_hook(d): new_d = {} for key, value in d.items(): @@ -150,7 +140,7 @@ def underscore_to_camelcase(s): @functools.cache def is_testing(argv=None): '''Return True if running django or py.test unit tests.''' - if 'PYTEST_CURRENT_TEST' in os.environ.keys(): + if os.environ.get('DJANGO_SETTINGS_MODULE') == 'awx.main.tests.settings_for_test': return True argv = sys.argv if argv is None else argv if len(argv) >= 1 and ('py.test' in argv[0] or 'py/test.py' in argv[0]): @@ -160,6 +150,14 @@ def is_testing(argv=None): return False +def bypass_in_test(func): + def fn(*args, **kwargs): + if not is_testing(): + return func(*args, **kwargs) + + return fn + + class RequireDebugTrueOrTest(logging.Filter): """ Logging filter to output when in DEBUG mode or running tests. @@ -235,9 +233,7 @@ def get_awx_version(): from awx import __version__ try: - import pkg_resources - - return pkg_resources.require('awx')[0].version + return _get_version('awx') except Exception: return __version__ @@ -367,7 +363,7 @@ def get_allowed_fields(obj, serializer_mapping): else: allowed_fields = [x.name for x in obj._meta.fields] - ACTIVITY_STREAM_FIELD_EXCLUSIONS = {'user': ['last_login'], 'oauth2accesstoken': ['last_used'], 'oauth2application': ['client_secret']} + ACTIVITY_STREAM_FIELD_EXCLUSIONS = {'user': ['last_login']} model_name = obj._meta.model_name fields_excluded = ACTIVITY_STREAM_FIELD_EXCLUSIONS.get(model_name, []) # see definition of from_db for CredentialType @@ -569,14 +565,6 @@ def copy_m2m_relationships(obj1, obj2, fields, kwargs=None): dest_field.add(*list(src_field_value.all().values_list('id', flat=True))) -def get_type_for_model(model): - """ - Return type name for a given model class. - """ - opts = model._meta.concrete_model._meta - return camelcase_to_underscore(opts.object_name) - - def get_model_for_type(type_name): """ Return model class for a given type name. @@ -717,7 +705,7 @@ def parse_yaml_or_json(vars_str, silent_failure=True): if silent_failure: return {} raise ParseError( - _('Cannot parse as JSON (error: {json_error}) or ' 'YAML (error: {yaml_error}).').format(json_error=str(json_err), yaml_error=str(yaml_err)) + _('Cannot parse as JSON (error: {json_error}) or YAML (error: {yaml_error}).').format(json_error=str(json_err), yaml_error=str(yaml_err)) ) return vars_dict @@ -768,14 +756,13 @@ def get_corrected_cpu(cpu_count): # formerlly get_cpu_capacity return cpu_count # no correction -def get_cpu_effective_capacity(cpu_count): +def get_cpu_effective_capacity(cpu_count, is_control_node=False): from django.conf import settings - cpu_count = get_corrected_cpu(cpu_count) - settings_forkcpu = getattr(settings, 'SYSTEM_TASK_FORKS_CPU', None) env_forkcpu = os.getenv('SYSTEM_TASK_FORKS_CPU', None) - + if is_control_node: + cpu_count = get_corrected_cpu(cpu_count) if env_forkcpu: forkcpu = int(env_forkcpu) elif settings_forkcpu: @@ -834,6 +821,7 @@ def get_corrected_memory(memory): # Runner returns memory in bytes # so we convert memory from settings to bytes as well. + if env_absmem is not None: return convert_mem_str_to_bytes(env_absmem) elif settings_absmem is not None: @@ -842,14 +830,13 @@ def get_corrected_memory(memory): return memory -def get_mem_effective_capacity(mem_bytes): +def get_mem_effective_capacity(mem_bytes, is_control_node=False): from django.conf import settings - mem_bytes = get_corrected_memory(mem_bytes) - settings_mem_mb_per_fork = getattr(settings, 'SYSTEM_TASK_FORKS_MEM', None) env_mem_mb_per_fork = os.getenv('SYSTEM_TASK_FORKS_MEM', None) - + if is_control_node: + mem_bytes = get_corrected_memory(mem_bytes) if env_mem_mb_per_fork: mem_mb_per_fork = int(env_mem_mb_per_fork) elif settings_mem_mb_per_fork: @@ -1013,9 +1000,15 @@ def getattrd(obj, name, default=NoDefaultProvided): raise -def getattr_dne(obj, name, notfound=ObjectDoesNotExist): +empty = object() + + +def getattr_dne(obj, name, default=empty, notfound=ObjectDoesNotExist): try: - return getattr(obj, name) + if default is empty: + return getattr(obj, name) + else: + return getattr(obj, name, default) except notfound: return None @@ -1089,29 +1082,6 @@ def has_model_field_prefetched(model_obj, field_name): return getattr(getattr(model_obj, field_name, None), 'prefetch_cache_name', '') in getattr(model_obj, '_prefetched_objects_cache', {}) -def get_external_account(user): - from django.conf import settings - - account_type = None - if getattr(settings, 'AUTH_LDAP_SERVER_URI', None): - try: - if user.pk and user.profile.ldap_dn and not user.has_usable_password(): - account_type = "ldap" - except AttributeError: - pass - if ( - getattr(settings, 'SOCIAL_AUTH_GOOGLE_OAUTH2_KEY', None) - or getattr(settings, 'SOCIAL_AUTH_GITHUB_KEY', None) - or getattr(settings, 'SOCIAL_AUTH_GITHUB_ORG_KEY', None) - or getattr(settings, 'SOCIAL_AUTH_GITHUB_TEAM_KEY', None) - or getattr(settings, 'SOCIAL_AUTH_SAML_ENABLED_IDPS', None) - ) and user.social_auth.all(): - account_type = "social" - if (getattr(settings, 'RADIUS_SERVER', None) or getattr(settings, 'TACACSPLUS_HOST', None)) and user.enterprise_auth.all(): - account_type = "enterprise" - return account_type - - class classproperty: def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget @@ -1134,7 +1104,11 @@ def create_temporary_fifo(data): path = os.path.join(tempfile.mkdtemp(), next(tempfile._get_candidate_names())) os.mkfifo(path, stat.S_IRUSR | stat.S_IWUSR) - threading.Thread(target=lambda p, d: open(p, 'wb').write(d), args=(path, data)).start() + def tmp_write(path, data): + with open(path, 'wb') as f: + f.write(data) + + threading.Thread(target=tmp_write, args=(path, data)).start() return path @@ -1172,6 +1146,17 @@ def deepmerge(a, b): return b +def table_exists(cursor, table_name): + cursor.execute(f"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = '{table_name}');") + row = cursor.fetchone() + if row is not None: + for val in row: # should only have 1 + if val is True: + logger.debug(f'Event partition table {table_name} already exists') + return True + return False + + def create_partition(tblname, start=None): """Creates new partition table for events. By default it covers the current hour.""" if start is None: @@ -1188,13 +1173,37 @@ def create_partition(tblname, start=None): try: with transaction.atomic(): with connection.cursor() as cursor: + if table_exists(cursor, f"{tblname}_{partition_label}"): + return + cursor.execute( - f'CREATE TABLE IF NOT EXISTS {tblname}_{partition_label} ' - f'PARTITION OF {tblname} ' - f'FOR VALUES FROM (\'{start_timestamp}\') to (\'{end_timestamp}\');' + f'CREATE TABLE {tblname}_{partition_label} (LIKE {tblname} INCLUDING DEFAULTS INCLUDING CONSTRAINTS); ' + f'ALTER TABLE {tblname} ATTACH PARTITION {tblname}_{partition_label} ' + f'FOR VALUES FROM (\'{start_timestamp}\') TO (\'{end_timestamp}\');' ) - except ProgrammingError as e: - logger.debug(f'Caught known error due to existing partition: {e}') + + except (ProgrammingError, IntegrityError) as e: + cause = e.__cause__ + if cause and hasattr(cause, 'sqlstate'): + sqlstate = cause.sqlstate + if sqlstate is None: + raise + sqlstate_cls = psycopg.errors.lookup(sqlstate) + + if sqlstate_cls in (psycopg.errors.DuplicateTable, psycopg.errors.DuplicateObject, psycopg.errors.UniqueViolation): + logger.info(f'Caught known error due to partition creation race: {e}') + else: + logger.error('SQL Error state: {} - {}'.format(sqlstate, sqlstate_cls)) + raise + except DatabaseError as e: + cause = e.__cause__ + if cause and hasattr(cause, 'sqlstate'): + sqlstate = cause.sqlstate + if sqlstate is None: + raise + sqlstate_str = psycopg.errors.lookup(sqlstate) + logger.error('SQL Error state: {} - {}'.format(sqlstate, sqlstate_str)) + raise def cleanup_new_process(func): @@ -1214,32 +1223,44 @@ def wrapper_cleanup_new_process(*args, **kwargs): return wrapper_cleanup_new_process -def log_excess_runtime(func_logger, cutoff=5.0, debug_cutoff=5.0, msg=None, add_log_data=False): - def log_excess_runtime_decorator(func): - @functools.wraps(func) - def _new_func(*args, **kwargs): - start_time = time.time() - log_data = {'name': repr(func.__name__)} +def unified_job_class_to_event_table_name(job_class): + return f'main_{job_class().event_class.__name__.lower()}' - if add_log_data: - return_value = func(*args, log_data=log_data, **kwargs) - else: - return_value = func(*args, **kwargs) - log_data['delta'] = time.time() - start_time - if isinstance(return_value, dict): - log_data.update(return_value) +def load_all_entry_points_for(entry_point_subsections: list[str], /) -> dict[str, EntryPoint]: + return {ep.name: ep for entry_point_category in entry_point_subsections for ep in entry_points(group=f'awx_plugins.{entry_point_category}')} - if msg is None: - record_msg = 'Running {name} took {delta:.2f}s' - else: - record_msg = msg - if log_data['delta'] > cutoff: - func_logger.info(record_msg.format(**log_data)) - elif log_data['delta'] > debug_cutoff: - func_logger.debug(record_msg.format(**log_data)) - return return_value - return _new_func +def get_auto_max_workers(): + """Method we normally rely on to get max_workers + + Uses almost same logic as Instance.local_health_check + The important thing is to be MORE than Instance.capacity + so that the task-manager does not over-schedule this node + + Ideally we would just use the capacity from the database plus reserve workers, + but this poses some bootstrap problems where OCP task containers + register themselves after startup + """ + # Get memory from ansible-runner + total_memory_gb = get_mem_in_bytes() + + # This may replace memory calculation with a user override + corrected_memory = get_corrected_memory(total_memory_gb) + + # Get same number as max forks based on memory, this function takes memory as bytes + mem_capacity = get_mem_effective_capacity(corrected_memory, is_control_node=True) + + # Follow same process for CPU capacity constraint + cpu_count = get_cpu_count() + corrected_cpu = get_corrected_cpu(cpu_count) + cpu_capacity = get_cpu_effective_capacity(corrected_cpu, is_control_node=True) + + # Here is what is different from health checks, + auto_max = max(mem_capacity, cpu_capacity) + + # add magic number of extra workers to ensure + # we have a few extra workers to run the heartbeat + auto_max += 7 - return log_excess_runtime_decorator + return auto_max diff --git a/awx/main/utils/db.py b/awx/main/utils/db.py index 5574d4ea91f2..2078b28d49fb 100644 --- a/awx/main/utils/db.py +++ b/awx/main/utils/db.py @@ -1,20 +1,34 @@ # Copyright (c) 2017 Ansible by Red Hat # All Rights Reserved. -from itertools import chain - - -def get_all_field_names(model): - # Implements compatibility with _meta.get_all_field_names - # See: https://docs.djangoproject.com/en/1.11/ref/models/meta/#migrating-from-the-old-api - return list( - set( - chain.from_iterable( - (field.name, field.attname) if hasattr(field, 'attname') else (field.name,) - for field in model._meta.get_fields() - # For complete backwards compatibility, you may want to exclude - # GenericForeignKey from the results. - if not (field.many_to_one and field.related_model is None) - ) - ) - ) +from awx.settings.application_name import set_application_name + +from django.conf import settings + + +def set_connection_name(function): + set_application_name(settings.DATABASES, settings.CLUSTER_HOST_ID, function=function) + + +def bulk_update_sorted_by_id(model, objects, fields, batch_size=1000): + """ + Perform a sorted bulk update on model instances to avoid database deadlocks. + + This function was introduced to prevent deadlocks observed in the AWX Controller + when concurrent jobs attempt to update different fields on the same `main_hosts` table. + Specifically, deadlocks occurred when one process updated `last_job_id` while another + simultaneously updated `ansible_facts`. + + By sorting updates ID, we ensure a consistent update order, + which helps avoid the row-level locking contention that can lead to deadlocks + in PostgreSQL when multiple processes are involved. + + Returns: + int: The number of rows affected by the update. + """ + objects = [obj for obj in objects if obj.id is not None] + if not objects: + return 0 # Return 0 when nothing is updated + + sorted_objects = sorted(objects, key=lambda obj: obj.id) + return model.objects.bulk_update(sorted_objects, fields, batch_size=batch_size) diff --git a/awx/main/utils/encryption.py b/awx/main/utils/encryption.py index 4272e3e07fc1..d23685d33456 100644 --- a/awx/main/utils/encryption.py +++ b/awx/main/utils/encryption.py @@ -9,7 +9,6 @@ from cryptography.hazmat.backends import default_backend from django.utils.encoding import smart_str, smart_bytes - __all__ = ['get_encryption_key', 'encrypt_field', 'decrypt_field', 'encrypt_value', 'decrypt_value', 'encrypt_dict'] logger = logging.getLogger('awx.main.utils.encryption') diff --git a/awx/main/utils/execution_environments.py b/awx/main/utils/execution_environments.py index 02e6a8b701f6..111b76acc637 100644 --- a/awx/main/utils/execution_environments.py +++ b/awx/main/utils/execution_environments.py @@ -1,13 +1,18 @@ -import os -from pathlib import Path +import logging from django.conf import settings from awx.main.models.execution_environments import ExecutionEnvironment +logger = logging.getLogger(__name__) + def get_control_plane_execution_environment(): - return ExecutionEnvironment.objects.filter(organization=None, managed=True).first() + ee = ExecutionEnvironment.objects.filter(organization=None, managed=True).first() + if ee == None: + logger.error('Failed to find control plane ee, there are no managed EEs without organizations') + raise RuntimeError("Failed to find default control plane EE") + return ee def get_default_execution_environment(): @@ -22,6 +27,7 @@ def get_default_execution_environment(): def get_default_pod_spec(): + job_label: str = settings.AWX_CONTAINER_GROUP_DEFAULT_JOB_LABEL ee = get_default_execution_environment() if ee is None: raise RuntimeError("Unable to find an execution environment.") @@ -29,10 +35,30 @@ def get_default_pod_spec(): return { "apiVersion": "v1", "kind": "Pod", - "metadata": {"namespace": settings.AWX_CONTAINER_GROUP_DEFAULT_NAMESPACE}, + "metadata": {"namespace": settings.AWX_CONTAINER_GROUP_DEFAULT_NAMESPACE, "labels": {job_label: ""}}, "spec": { "serviceAccountName": "default", "automountServiceAccountToken": False, + "affinity": { + "podAntiAffinity": { + "preferredDuringSchedulingIgnoredDuringExecution": [ + { + "weight": 100, + "podAffinityTerm": { + "labelSelector": { + "matchExpressions": [ + { + "key": job_label, + "operator": "Exists", + } + ] + }, + "topologyKey": "kubernetes.io/hostname", + }, + } + ] + } + }, "containers": [ { "image": ee.image, @@ -43,24 +69,3 @@ def get_default_pod_spec(): ], }, } - - -# this is the root of the private data dir as seen from inside -# of the container running a job -CONTAINER_ROOT = '/runner' - - -def to_container_path(path, private_data_dir): - """Given a path inside of the host machine filesystem, - this returns the expected path which would be observed by the job running - inside of the EE container. - This only handles the volume mount from private_data_dir to /runner - """ - if not os.path.isabs(private_data_dir): - raise RuntimeError('The private_data_dir path must be absolute') - # due to how tempfile.mkstemp works, we are probably passed a resolved path, but unresolved private_data_dir - resolved_path = Path(path).resolve() - resolved_pdd = Path(private_data_dir).resolve() - if resolved_pdd != resolved_path and resolved_pdd not in resolved_path.parents: - raise RuntimeError(f'Cannot convert path {resolved_path} unless it is a subdir of {resolved_pdd}') - return str(resolved_path).replace(str(resolved_pdd), CONTAINER_ROOT, 1) diff --git a/awx/main/utils/external_logging.py b/awx/main/utils/external_logging.py index 26f434a4e439..ff98123febbc 100644 --- a/awx/main/utils/external_logging.py +++ b/awx/main/utils/external_logging.py @@ -4,6 +4,7 @@ import urllib.parse as urlparse from django.conf import settings +from dispatcherd.publish import task from awx.main.utils.reload import supervisor_service_command @@ -16,10 +17,26 @@ def construct_rsyslog_conf_template(settings=settings): port = getattr(settings, 'LOG_AGGREGATOR_PORT', '') protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', '') timeout = getattr(settings, 'LOG_AGGREGATOR_TCP_TIMEOUT', 5) - max_disk_space = getattr(settings, 'LOG_AGGREGATOR_MAX_DISK_USAGE_GB', 1) + action_queue_size = getattr(settings, 'LOG_AGGREGATOR_ACTION_QUEUE_SIZE', 131072) + max_disk_space_action_queue = getattr(settings, 'LOG_AGGREGATOR_ACTION_MAX_DISK_USAGE_GB', 1) spool_directory = getattr(settings, 'LOG_AGGREGATOR_MAX_DISK_USAGE_PATH', '/var/lib/awx').rstrip('/') error_log_file = getattr(settings, 'LOG_AGGREGATOR_RSYSLOGD_ERROR_LOG_FILE', '') + queue_options = [ + f'queue.spoolDirectory="{spool_directory}"', + 'queue.filename="awx-external-logger-action-queue"', + f'queue.maxDiskSpace="{max_disk_space_action_queue}g"', # overall disk space for all queue files + 'queue.maxFileSize="100m"', # individual file size + 'queue.type="LinkedList"', + 'queue.saveOnShutdown="on"', + 'queue.syncqueuefiles="on"', # (f)sync when checkpoint occurs + 'queue.checkpointInterval="1000"', # Update disk queue every 1000 messages + f'queue.size="{action_queue_size}"', # max number of messages in queue + f'queue.highwaterMark="{int(action_queue_size * 0.75)}"', # 75% of queue.size + f'queue.discardMark="{int(action_queue_size * 0.9)}"', # 90% of queue.size + 'queue.discardSeverity="5"', # Only discard notice, info, debug if we must discard anything + ] + if not os.access(spool_directory, os.W_OK): spool_directory = '/var/lib/awx' @@ -31,7 +48,6 @@ def construct_rsyslog_conf_template(settings=settings): '$WorkDirectory /var/lib/awx/rsyslog', f'$MaxMessageSize {max_bytes}', '$IncludeConfig /var/lib/awx/rsyslog/conf.d/*.conf', - f'main_queue(queue.spoolDirectory="{spool_directory}" queue.maxdiskspace="{max_disk_space}g" queue.type="Disk" queue.filename="awx-external-logger-backlog")', # noqa 'module(load="imuxsock" SysSock.Use="off")', 'input(type="imuxsock" Socket="' + settings.LOGGING['handlers']['external_logger']['address'] + '" unlink="on" RateLimit.Burst="0")', 'template(name="awx" type="string" string="%rawmsg-after-pri%")', @@ -39,6 +55,8 @@ def construct_rsyslog_conf_template(settings=settings): ) def escape_quotes(x): + if x is None: + return '' return x.replace('"', '\\"') if not enabled: @@ -77,7 +95,7 @@ def escape_quotes(x): 'action.resumeRetryCount="-1"', 'template="awx"', f'action.resumeInterval="{timeout}"', - ] + ] + queue_options if error_log_file: params.append(f'errorfile="{error_log_file}"') if parsed.path: @@ -105,15 +123,25 @@ def escape_quotes(x): params = ' '.join(params) parts.extend(['module(load="omhttp")', f'action({params})']) elif protocol and host and port: - parts.append( - f'action(type="omfwd" target="{host}" port="{port}" protocol="{protocol}" action.resumeRetryCount="-1" action.resumeInterval="{timeout}" template="awx")' # noqa - ) + params = [ + 'type="omfwd"', + f'target="{host}"', + f'port="{port}"', + f'protocol="{protocol}"', + 'action.resumeRetryCount="-1"', + f'action.resumeInterval="{timeout}"', + 'template="awx"', + ] + queue_options + params = ' '.join(params) + parts.append(f'action({params})') + else: parts.append('action(type="omfile" file="/dev/null")') # rsyslog needs *at least* one valid action to start tmpl = '\n'.join(parts) return tmpl +@task(queue='rsyslog_configurer', timeout=600, on_duplicate='queue_one') def reconfigure_rsyslog(): tmpl = construct_rsyslog_conf_template() # Write config to a temp file then move it to preserve atomicity diff --git a/awx/main/utils/filters.py b/awx/main/utils/filters.py index 7f9724329b80..389b1f93c401 100644 --- a/awx/main/utils/filters.py +++ b/awx/main/utils/filters.py @@ -1,5 +1,7 @@ import re from functools import reduce + +from django.core.exceptions import FieldDoesNotExist from pyparsing import ( infixNotation, opAssoc, @@ -161,7 +163,7 @@ def __init__(self, t): else: # detect loops and restrict access to sensitive fields # this import is intentional here to avoid a circular import - from awx.api.filters import FieldLookupBackend + from ansible_base.rest_filters.rest_framework.field_lookup_backend import FieldLookupBackend FieldLookupBackend().get_field_from_lookup(Host, k) kwargs[k] = v @@ -353,7 +355,7 @@ def query_from_string(cls, filter_string): try: res = boolExpr.parseString('(' + filter_string + ')') - except ParseException: + except (ParseException, FieldDoesNotExist): raise RuntimeError(u"Invalid query %s" % filter_string_raw) if len(res) > 0: diff --git a/awx/main/utils/formatters.py b/awx/main/utils/formatters.py index 783278bd9eb7..45ff3f0d955c 100644 --- a/awx/main/utils/formatters.py +++ b/awx/main/utils/formatters.py @@ -3,7 +3,6 @@ from copy import copy import json -import json_log_formatter import logging import traceback import socket @@ -15,15 +14,6 @@ from django.conf import settings -class JobLifeCycleFormatter(json_log_formatter.JSONFormatter): - def json_record(self, message: str, extra: dict, record: logging.LogRecord): - if 'time' not in extra: - extra['time'] = now() - if record.exc_info: - extra['exc_info'] = self.formatException(record.exc_info) - return extra - - class TimeFormatter(logging.Formatter): """ Custom log formatter used for inventory imports @@ -170,6 +160,11 @@ def reformat_data_for_log(self, raw_data, kind=None): data = json.loads(data) data_for_log = {} + # For the job_lifecycle logger, copy some raw data fields directly + for key in ('lifecycle_data', 'organization_id'): + if key in raw_data: + data_for_log[key] = raw_data[key] + if kind == 'job_events' and raw_data.get('python_objects', {}).get('job_event'): job_event = raw_data['python_objects']['job_event'] guid = job_event.event_data.pop('guid', None) @@ -262,8 +257,7 @@ def get_extra_fields(self, record): return fields def format(self, record): - stamp = datetime.utcfromtimestamp(record.created) - stamp = stamp.replace(tzinfo=tzutc()) + stamp = datetime.fromtimestamp(record.created, tz=tzutc()) message = { # Field not included, but exist in related logs # 'path': record.pathname @@ -283,6 +277,7 @@ def format(self, record): message.update(self.get_debug_fields(record)) if settings.LOG_AGGREGATOR_TYPE == 'splunk': - # splunk messages must have a top level "event" key - message = {'event': message} + # splunk messages must have a top level "event" key when using the /services/collector/event receiver. + # The event receiver wont scan an event for a timestamp field therefore a time field must also be supplied containing epoch timestamp + message = {'time': record.created, 'event': message} return self.serialize(message) diff --git a/awx/main/utils/handlers.py b/awx/main/utils/handlers.py index aa32c77e8c7f..f6209c755ec1 100644 --- a/awx/main/utils/handlers.py +++ b/awx/main/utils/handlers.py @@ -2,10 +2,13 @@ # All Rights Reserved. # Python +import base64 import logging +import logging.handlers import sys import traceback -from datetime import datetime +import os +from datetime import datetime, timezone # Django from django.conf import settings @@ -15,6 +18,17 @@ # AWX from awx.main.exceptions import PostRunError +# OTEL +from opentelemetry._logs import set_logger_provider +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter as OTLPGrpcLogExporter +from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter as OTLPHttpLogExporter + +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.resources import Resource + +__all__ = ['RSysLogHandler', 'SpecialInventoryHandler', 'ColorHandler'] + class RSysLogHandler(logging.handlers.SysLogHandler): append_nul = False @@ -35,7 +49,7 @@ def handleError(self, record): # because the alternative is blocking the # socket.send() in the Python process, which we definitely don't # want to do) - dt = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S') + dt = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S') msg = f'{dt} ERROR rsyslogd was unresponsive: ' exc = traceback.format_exc() try: @@ -97,39 +111,71 @@ def emit(self, record): self.event_handler(dispatch_data) -ColorHandler = logging.StreamHandler - if settings.COLOR_LOGS is True: - try: - from logutils.colorize import ColorizingStreamHandler - import colorama - - colorama.deinit() - colorama.init(wrap=False, convert=False, strip=False) - - class ColorHandler(ColorizingStreamHandler): - def colorize(self, line, record): - # comment out this method if you don't like the job_lifecycle - # logs rendered with cyan text - previous_level_map = self.level_map.copy() - if record.name == "awx.analytics.job_lifecycle": - self.level_map[logging.INFO] = (None, 'cyan', True) - msg = super(ColorHandler, self).colorize(line, record) - self.level_map = previous_level_map - return msg - - def format(self, record): - message = logging.StreamHandler.format(self, record) - return '\n'.join([self.colorize(line, record) for line in message.splitlines()]) - - level_map = { - logging.DEBUG: (None, 'green', True), - logging.INFO: (None, None, True), - logging.WARNING: (None, 'yellow', True), - logging.ERROR: (None, 'red', True), - logging.CRITICAL: (None, 'red', True), - } - - except ImportError: - # logutils is only used for colored logs in the dev environment - pass + from logutils.colorize import ColorizingStreamHandler + import colorama + + colorama.deinit() + colorama.init(wrap=False, convert=False, strip=False) + + class ColorHandler(ColorizingStreamHandler): + def colorize(self, line, record): + # comment out this method if you don't like the job_lifecycle + # logs rendered with cyan text + previous_level_map = self.level_map.copy() + if record.name == "awx.analytics.job_lifecycle": + self.level_map[logging.INFO] = (None, 'cyan', True) + msg = super(ColorHandler, self).colorize(line, record) + self.level_map = previous_level_map + return msg + + def format(self, record): + message = logging.StreamHandler.format(self, record) + return '\n'.join([self.colorize(line, record) for line in message.splitlines()]) + + level_map = { + logging.DEBUG: (None, 'green', True), + logging.INFO: (None, None, True), + logging.WARNING: (None, 'yellow', True), + logging.ERROR: (None, 'red', True), + logging.CRITICAL: (None, 'red', True), + } + +else: + ColorHandler = logging.StreamHandler + + +class OTLPHandler(LoggingHandler): + def __init__(self, endpoint=None, protocol='grpc', service_name=None, instance_id=None, auth=None, username=None, password=None): + if not endpoint: + raise ValueError("endpoint required") + + if auth == 'basic' and (username is None or password is None): + raise ValueError("auth type basic requires username and passsword parameters") + + self.endpoint = endpoint + self.service_name = service_name or (sys.argv[1] if len(sys.argv) > 1 else (sys.argv[0] or 'unknown_service')) + self.instance_id = instance_id or os.uname().nodename + + logger_provider = LoggerProvider( + resource=Resource.create( + { + "service.name": self.service_name, + "service.instance.id": self.instance_id, + } + ), + ) + set_logger_provider(logger_provider) + + headers = {} + if auth == 'basic': + secret = f'{username}:{password}' + headers['Authorization'] = "Basic " + base64.b64encode(secret.encode()).decode() + + if protocol == 'grpc': + otlp_exporter = OTLPGrpcLogExporter(endpoint=self.endpoint, insecure=True, headers=headers) + elif protocol == 'http': + otlp_exporter = OTLPHttpLogExporter(endpoint=self.endpoint, headers=headers) + logger_provider.add_log_record_processor(BatchLogRecordProcessor(otlp_exporter)) + + super().__init__(level=logging.NOTSET, logger_provider=logger_provider) diff --git a/awx/main/utils/inventory_vars.py b/awx/main/utils/inventory_vars.py new file mode 100644 index 000000000000..7780a93c73ff --- /dev/null +++ b/awx/main/utils/inventory_vars.py @@ -0,0 +1,276 @@ +import logging +from typing import TypeAlias, Any + +from awx.main.models import InventoryGroupVariablesWithHistory + +var_value: TypeAlias = Any +update_queue: TypeAlias = list[tuple[int, var_value]] + + +logger = logging.getLogger('awx.api.inventory_import') + + +class InventoryVariable: + """ + Represents an inventory variable. + + This class keeps track of the variable updates from different inventory + sources. + """ + + def __init__(self, name: str) -> None: + """ + :param str name: The variable's name. + :return: None + """ + self.name = name + self._update_queue: update_queue = [] + """ + A queue representing updates from inventory sources in the sequence of + occurrence. + + The queue is realized as a list of two-tuples containing variable values + and their originating inventory source. The last item of the list is + considered the top of the queue, and holds the current value of the + variable. + """ + + def reset(self) -> None: + """Reset the variable by deleting its history.""" + self._update_queue = [] + + def load(self, updates: update_queue) -> "InventoryVariable": + """Load internal state from a list.""" + self._update_queue = updates + return self + + def dump(self) -> update_queue: + """Save internal state to a list.""" + return self._update_queue + + def update(self, value: var_value, invsrc_id: int) -> None: + """ + Update the variable with a new value from an inventory source. + + Updating means that this source is moved to the top of the queue + and `value` becomes the new current value. + + :param value: The new value of the variable. + :param int invsrc_id: The inventory source of the new variable value. + :return: None + """ + logger.debug(f"InventoryVariable().update({value}, {invsrc_id}):") + # Move this source to the front of the queue by first deleting a + # possibly existing entry, and then add the new entry to the front. + self.delete(invsrc_id) + self._update_queue.append((invsrc_id, value)) + + def delete(self, invsrc_id: int) -> None: + """ + Delete an inventory source from the variable. + + :param int invsrc_id: The inventory source id. + :return: None + """ + data_index = self._get_invsrc_index(invsrc_id) + # Remove last update from this source, if there was any. + if data_index is not None: + value = self._update_queue.pop(data_index)[1] + logger.debug(f"InventoryVariable().delete({invsrc_id}): {data_index=} {value=}") + + def _get_invsrc_index(self, invsrc_id: int) -> int | None: + """Return the inventory source's position in the queue, or `None`.""" + for i, entry in enumerate(self._update_queue): + if entry[0] == invsrc_id: + return i + return None + + def _get_current_value(self) -> var_value: + """ + Return the current value of the variable, or None if the variable has no + history. + """ + return self._update_queue[-1][1] if self._update_queue else None + + @property + def value(self) -> var_value: + """Read the current value of the variable.""" + return self._get_current_value() + + @property + def has_no_source(self) -> bool: + """True, if the variable is orphan, i.e. no source contains this var anymore.""" + return not self._update_queue + + def __str__(self): + """Return the string representation of the current value.""" + return str(self.value or "") + + +class InventoryGroupVariables(dict): + """ + Represent all inventory variables from one group. + + This dict contains all variables of a inventory group and their current + value under consideration of the inventory source update history. + + Note that variables values cannot be `None`, use the empty string to + indicate that a variable holds no value. See also `InventoryVariable`. + """ + + def __init__(self, id: int) -> None: + """ + :param int id: The id of the group object. + :return: None + """ + super().__init__() + self.id = id + # In _vars we keep all sources for a given variable. This enables us to + # find the current value for a variable, which is the value from the + # latest update which defined this variable. + self._vars: dict[str, InventoryVariable] = {} + + def _sync_vars(self) -> None: + """ + Copy the current values of all variables into the internal dict. + + Call this everytime the `_vars` structure has been modified. + """ + for name, inv_var in self._vars.items(): + self[name] = inv_var.value + + def load_state(self, state: dict[str, update_queue]) -> "InventoryGroupVariables": + """Load internal state from a dict.""" + for name, updates in state.items(): + self._vars[name] = InventoryVariable(name).load(updates) + self._sync_vars() + return self + + def save_state(self) -> dict[str, update_queue]: + """Return internal state as a dict.""" + state = {} + for name, inv_var in self._vars.items(): + state[name] = inv_var.dump() + return state + + def update_from_src( + self, + new_vars: dict[str, var_value], + source_id: int, + overwrite_vars: bool = True, + reset: bool = False, + ) -> None: + """ + Update with variables from an inventory source. + + Delete all variables for this source which are not in the update vars. + + :param dict new_vars: The variables from the inventory source. + :param int invsrc_id: The id of the inventory source for this update. + :param bool overwrite_vars: If `True`, delete this source's history + entry for variables which are not in this update. If `False`, keep + the old updates in the history for such variables. Default is + `True`. + :param bool reset: If `True`, delete the update history for all existing + variables before updating the new vars. Therewith making this update + overwrite all history. Default is `False`. + :return: None + """ + logger.debug(f"InventoryGroupVariables({self.id}).update_from_src({new_vars=}, {source_id=}, {overwrite_vars=}, {reset=}): {self=}") + # Create variables which are newly introduced by this source. + for name in new_vars: + if name not in self._vars: + self._vars[name] = InventoryVariable(name) + # Combine the names of the existing vars and the new vars from this update. + all_var_names = list(set(list(self.keys()) + list(new_vars.keys()))) + # In reset-mode, delete all existing vars and their history before + # updating. + if reset: + for name in all_var_names: + self._vars[name].reset() + # Go through all variables (the existing ones, and the ones added by + # this update), delete this source from variables which are not in this + # update, and update the value of variables which are part of this + # update. + for name in all_var_names: + # Update or delete source from var (if name not in vars). + if name in new_vars: + self._vars[name].update(new_vars[name], source_id) + elif overwrite_vars: + self._vars[name].delete(source_id) + # Delete vars which have no source anymore. + if self._vars[name].has_no_source: + del self._vars[name] + del self[name] + # After the update, refresh the internal dict with the possibly changed + # current values. + self._sync_vars() + logger.debug(f"InventoryGroupVariables({self.id}).update_from_src(): {self=}") + + +def update_group_variables( + group_id: int | None, + newvars: dict, + dbvars: dict | None, + invsrc_id: int, + inventory_id: int, + overwrite_vars: bool = True, + reset: bool = False, +) -> dict[str, var_value]: + """ + Update the inventory variables of one group. + + Merge the new variables into the existing group variables. + + The update can be triggered either by an inventory update via API, or via a + manual edit of the variables field in the awx inventory form. + + TODO: Can we get rid of the dbvars? This is only needed because the new + update-var mechanism needs to be properly initialized if the db already + contains some variables. + + :param int group_id: The inventory group id (pk). For the 'all'-group use + `None`, because this group is not an actual `Group` object in the + database. + :param dict newvars: The variables contained in this update. + :param dict dbvars: The variables which are already stored in the database + for this inventory and this group. Can be `None`. + :param int invsrc_id: The id of the inventory source. Usually this is the + database primary key of the inventory source object, but there is one + special id -1 which is used for the initial update from the database and + for manual updates via the GUI. + :param int inventory_id: The id of the inventory on which this update is + applied. + :param bool overwrite_vars: If `True`, delete variables which were merged + from the same source in a previous update, but are no longer contained + in that source. If `False`, such variables would not be removed from the + group. Default is `True`. + :param bool reset: If `True`, delete all variables from previous updates, + therewith making this update overwrite all history. Default is `False`. + :return: The variables and their current values as a dict. + :rtype: dict + """ + inv_group_vars = InventoryGroupVariables(group_id) + # Restore the existing variables state. + try: + # Get the object for this group from the database. + model = InventoryGroupVariablesWithHistory.objects.get(inventory_id=inventory_id, group_id=group_id) + except InventoryGroupVariablesWithHistory.DoesNotExist: + # If no previous state exists, create a new database object, and + # initialize it with the current group variables. + model = InventoryGroupVariablesWithHistory(inventory_id=inventory_id, group_id=group_id) + if dbvars: + inv_group_vars.update_from_src(dbvars, -1) # Assume -1 as inv_source_id for existing vars. + else: + # Load the group variables state from the database object. + inv_group_vars.load_state(model.variables) + # + logger.debug(f"update_group_variables: before update_from_src {model.variables=}") + # Apply the new inventory update onto the group variables. + inv_group_vars.update_from_src(newvars, invsrc_id, overwrite_vars, reset) + # Save the new variables state. + model.variables = inv_group_vars.save_state() + model.save() + logger.debug(f"update_group_variables: after update_from_src {model.variables=}") + logger.debug(f"update_group_variables({group_id=}, {newvars}): {inv_group_vars}") + return inv_group_vars diff --git a/awx/main/utils/licensing.py b/awx/main/utils/licensing.py index bec953f822df..20417940b8bc 100644 --- a/awx/main/utils/licensing.py +++ b/awx/main/utils/licensing.py @@ -15,7 +15,6 @@ import collections import copy import io -import os import json import logging import re @@ -35,6 +34,11 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ +# Shared code for the AWX platform +from awx_plugins.interfaces._temporary_private_licensing_api import detect_server_product_name + +from awx.main.constants import SUBSCRIPTION_USAGE_MODEL_UNIQUE_HOSTS +from awx.main.utils.analytics_proxy import OIDCClient MAX_INSTANCES = 9999999 @@ -169,10 +173,17 @@ def _can_aggregate(sub, license): license.setdefault('sku', sub['pool']['productId']) license.setdefault('subscription_name', sub['pool']['productName']) + license.setdefault('subscription_id', sub['pool']['subscriptionId']) + license.setdefault('account_number', sub['pool']['accountNumber']) license.setdefault('pool_id', sub['pool']['id']) license.setdefault('product_name', sub['pool']['productName']) license.setdefault('valid_key', True) - license.setdefault('license_type', 'enterprise') + if sub['pool']['productId'].startswith('S'): + license.setdefault('trial', True) + license.setdefault('license_type', 'trial') + else: + license.setdefault('trial', False) + license.setdefault('license_type', 'enterprise') license.setdefault('satellite', False) # Use the nearest end date endDate = parse_date(sub['endDate']) @@ -184,6 +195,16 @@ def _can_aggregate(sub, license): license['instance_count'] = license.get('instance_count', 0) + instances license['subscription_name'] = re.sub(r'[\d]* Managed Nodes', '%d Managed Nodes' % license['instance_count'], license['subscription_name']) + license['support_level'] = '' + license['usage'] = '' + for attr in sub['pool'].get('productAttributes', []): + if attr.get('name') == 'support_level': + license['support_level'] = attr.get('value') + elif attr.get('name') == 'usage': + license['usage'] = attr.get('value') + elif attr.get('name') == 'ph_product_name' and attr.get('value') == 'RHEL Developer': + license['license_type'] = 'developer' + if not license: logger.error("No valid subscriptions found in manifest") self._attrs.update(license) @@ -198,27 +219,43 @@ def update(self, **kwargs): kwargs['license_date'] = int(kwargs['license_date']) self._attrs.update(kwargs) - def validate_rh(self, user, pw): + def get_host_from_rhsm_config(self): try: host = 'https://' + str(self.config.get("server", "hostname")) except Exception: logger.exception('Cannot access rhsm.conf, make sure subscription manager is installed and configured.') host = None + return host + + def validate_rh(self, user, pw, basic_auth): + # if basic auth is True, host is read from rhsm.conf (subscription.rhsm.redhat.com) + # if basic auth is False, host is settings.SUBSCRIPTIONS_RHSM_URL (console.redhat.com) + # if rhsm.conf is not found, host is settings.REDHAT_CANDLEPIN_HOST (satellite server) + if basic_auth: + host = self.get_host_from_rhsm_config() + if not host: + host = getattr(settings, 'REDHAT_CANDLEPIN_HOST', None) + else: + host = settings.SUBSCRIPTIONS_RHSM_URL + if not host: - host = getattr(settings, 'REDHAT_CANDLEPIN_HOST', None) + raise ValueError('Could not get host url for subscriptions') if not user: - raise ValueError('subscriptions_username is required') + raise ValueError('subscriptions_client_id or subscriptions_username is required') if not pw: - raise ValueError('subscriptions_password is required') + raise ValueError('subscriptions_client_secret or subscriptions_password is required') if host and user and pw: - if 'subscription.rhsm.redhat.com' in host: - json = self.get_rhsm_subs(host, user, pw) + if basic_auth: + if 'subscription.rhsm.redhat.com' in host: + json = self.get_rhsm_subs(host, user, pw) + else: + json = self.get_satellite_subs(host, user, pw) else: - json = self.get_satellite_subs(host, user, pw) - return self.generate_license_options_from_entitlements(json) + json = self.get_crc_subs(host, user, pw) + return self.generate_license_options_from_entitlements(json, is_candlepin=basic_auth) return [] def get_rhsm_subs(self, host, user, pw): @@ -240,6 +277,35 @@ def get_rhsm_subs(self, host, user, pw): json.extend(resp.json()) return json + def get_crc_subs(self, host, client_id, client_secret): + try: + client = OIDCClient(client_id, client_secret) + subs = client.make_request( + 'GET', + host, + verify=True, + timeout=(31, 31), + ) + except requests.RequestException: + logger.warning("Failed to connect to console.redhat.com using Service Account credentials. Falling back to basic auth.") + subs = requests.request( + 'GET', + host, + auth=(client_id, client_secret), + verify=True, + timeout=(31, 31), + ) + subs.raise_for_status() + subs_formatted = [] + for sku in subs.json()['body']: + sku_data = {k: v for k, v in sku.items() if k != 'subscriptions'} + for sub in sku['subscriptions']: + sub_data = sku_data.copy() + sub_data['subscriptions'] = sub + subs_formatted.append(sub_data) + + return subs_formatted + def get_satellite_subs(self, host, user, pw): port = None try: @@ -247,7 +313,7 @@ def get_satellite_subs(self, host, user, pw): port = str(self.config.get("server", "port")) except Exception as e: logger.exception('Unable to read rhsm config to get ca_cert location. {}'.format(str(e))) - verify = getattr(settings, 'REDHAT_CANDLEPIN_VERIFY', True) + verify = True if port: host = ':'.join([host, port]) json = [] @@ -276,7 +342,10 @@ def get_satellite_subs(self, host, user, pw): license['productId'] = sub['product_id'] license['quantity'] = int(sub['quantity']) license['support_level'] = sub['support_level'] + license['usage'] = sub.get('usage') license['subscription_name'] = sub['name'] + license['subscriptionId'] = sub['subscription_id'] + license['accountNumber'] = sub['account_number'] license['id'] = sub['upstream_pool_id'] license['endDate'] = sub['end_date'] license['productName'] = "Red Hat Ansible Automation" @@ -286,11 +355,6 @@ def get_satellite_subs(self, host, user, pw): json.append(license) return json - def is_appropriate_sat_sub(self, sub): - if 'Red Hat Ansible Automation' not in sub['subscription_name']: - return False - return True - def is_appropriate_sub(self, sub): if sub['activeSubscription'] is False: return False @@ -300,47 +364,94 @@ def is_appropriate_sub(self, sub): return True return False - def generate_license_options_from_entitlements(self, json): + def is_appropriate_sat_sub(self, sub): + if 'Red Hat Ansible Automation' not in sub['subscription_name']: + return False + return True + + def generate_license_options_from_entitlements(self, json, is_candlepin=False): from dateutil.parser import parse - ValidSub = collections.namedtuple('ValidSub', 'sku name support_level end_date trial quantity pool_id satellite') + ValidSub = collections.namedtuple( + 'ValidSub', 'sku name support_level end_date trial developer_license quantity satellite subscription_id account_number usage' + ) valid_subs = [] for sub in json: satellite = sub.get('satellite') if satellite: is_valid = self.is_appropriate_sat_sub(sub) - else: + elif is_candlepin: is_valid = self.is_appropriate_sub(sub) + else: + # the list of subs from console.redhat.com and subscriptions.rhsm.redhat.com are already valid based on the query params we provided + is_valid = True if is_valid: try: - end_date = parse(sub.get('endDate')) + if is_candlepin: + end_date = parse(sub.get('endDate')) + else: + end_date = parse(sub['subscriptions']['endDate']) except Exception: continue - now = datetime.utcnow() + now = datetime.now(timezone.utc) now = now.replace(tzinfo=end_date.tzinfo) if end_date < now: # If the sub has a past end date, skip it continue - try: - quantity = int(sub['quantity']) - if quantity == -1: - # effectively, unlimited - quantity = MAX_INSTANCES - except Exception: - continue - sku = sub['productId'] - trial = sku.startswith('S') # i.e.,, SER/SVC - support_level = '' - pool_id = sub['id'] - if satellite: - support_level = sub['support_level'] + developer_license = False + support_level = sub.get('support_level', '') + account_number = '' + usage = sub.get('usage', '') + if is_candlepin: + try: + quantity = int(sub['quantity']) + except Exception: + continue + sku = sub['productId'] + subscription_id = sub['subscriptionId'] + sub_name = sub['productName'] + account_number = sub['accountNumber'] else: - for attr in sub.get('productAttributes', []): - if attr.get('name') == 'support_level': - support_level = attr.get('value') + try: + # Determine total quantity based on capacity name + # if capacity name is Nodes, capacity quantity x subscription quantity + # if capacity name is Sockets, capacity quantity / 2 (minimum of 1) x subscription quantity + if sub['capacity']['name'] == "Nodes": + quantity = int(sub['capacity']['quantity']) * int(sub['subscriptions']['quantity']) + elif sub['capacity']['name'] == "Sockets": + quantity = max(int(sub['capacity']['quantity']) / 2, 1) * int(sub['subscriptions']['quantity']) + else: + continue + except Exception: + continue + sku = sub['sku'] + sub_name = sub['name'] + support_level = sub['serviceLevel'] + subscription_id = sub['subscriptions']['number'] + if sub.get('name') == 'RHEL Developer': + developer_license = True + + if quantity == -1: + # effectively, unlimited + quantity = MAX_INSTANCES + trial = sku.startswith('S') # i.e.,, SER/SVC - valid_subs.append(ValidSub(sku, sub['productName'], support_level, end_date, trial, quantity, pool_id, satellite)) + valid_subs.append( + ValidSub( + sku, + sub_name, + support_level, + end_date, + trial, + developer_license, + quantity, + satellite, + subscription_id, + account_number, + usage, + ) + ) if valid_subs: licenses = [] @@ -349,10 +460,13 @@ def generate_license_options_from_entitlements(self, json): license._attrs['instance_count'] = int(sub.quantity) license._attrs['sku'] = sub.sku license._attrs['support_level'] = sub.support_level + license._attrs['usage'] = sub.usage license._attrs['license_type'] = 'enterprise' if sub.trial: license._attrs['trial'] = True license._attrs['license_type'] = 'trial' + if sub.developer_license: + license._attrs['license_type'] = 'developer' license._attrs['instance_count'] = min(MAX_INSTANCES, license._attrs['instance_count']) human_instances = license._attrs['instance_count'] if human_instances == MAX_INSTANCES: @@ -362,8 +476,11 @@ def generate_license_options_from_entitlements(self, json): license._attrs['satellite'] = satellite license._attrs['valid_key'] = True license.update(license_date=int(sub.end_date.strftime('%s'))) - license.update(pool_id=sub.pool_id) + license.update(subscription_id=sub.subscription_id) + license.update(account_number=sub.account_number) licenses.append(license._attrs.copy()) + # sort by sku + licenses.sort(key=lambda x: x['sku']) return licenses raise ValueError('No valid Red Hat Ansible Automation subscription could be found for this account.') # noqa @@ -382,12 +499,28 @@ def validate(self): current_instances = Host.objects.active_count() license_date = int(attrs.get('license_date', 0) or 0) - automated_instances = HostMetric.objects.count() - first_host = HostMetric.objects.only('first_automation').order_by('first_automation').first() + + subscription_model = getattr(settings, 'SUBSCRIPTION_USAGE_MODEL', '') + if subscription_model == SUBSCRIPTION_USAGE_MODEL_UNIQUE_HOSTS: + automated_instances = HostMetric.active_objects.count() + first_host = HostMetric.active_objects.only('first_automation').order_by('first_automation').first() + attrs['deleted_instances'] = HostMetric.objects.filter(deleted=True).count() + attrs['reactivated_instances'] = HostMetric.active_objects.filter(deleted_counter__gte=1).count() + else: + automated_instances = 0 + first_host = HostMetric.objects.only('first_automation').order_by('first_automation').first() + attrs['deleted_instances'] = 0 + attrs['reactivated_instances'] = 0 + if first_host: automated_since = int(first_host.first_automation.timestamp()) else: - automated_since = int(Instance.objects.order_by('id').first().created.timestamp()) + try: + automated_since = int(Instance.objects.order_by('id').first().created.timestamp()) + except AttributeError: + # In the odd scenario that create_preload_data was not run, there are no hosts + # Then we CAN end up here before any instance has registered + automated_since = int(time.time()) instance_count = int(attrs.get('instance_count', 0)) attrs['current_instances'] = current_instances attrs['automated_instances'] = automated_instances @@ -412,13 +545,9 @@ def get_licenser(*args, **kwargs): from awx.main.utils.licensing import Licenser, OpenLicense try: - if os.path.exists('/var/lib/awx/.tower_version'): - return Licenser(*args, **kwargs) - else: + if detect_server_product_name() == 'AWX': return OpenLicense() + else: + return Licenser(*args, **kwargs) except Exception as e: raise ValueError(_('Error importing License: %s') % e) - - -def server_product_name(): - return 'AWX' if isinstance(get_licenser(), OpenLicense) else 'Red Hat Ansible Automation Platform' diff --git a/awx/main/utils/mem_inventory.py b/awx/main/utils/mem_inventory.py index 7e6e458cb8f1..3167710d8c5d 100644 --- a/awx/main/utils/mem_inventory.py +++ b/awx/main/utils/mem_inventory.py @@ -6,7 +6,6 @@ import logging from collections import OrderedDict - # Logger is used for any data-related messages so that the log level # can be adjusted on command invocation logger = logging.getLogger('awx.main.commands.inventory_import') @@ -253,7 +252,7 @@ def dict_to_mem_data(data, inventory=None): if isinstance(hv, dict): host.variables.update(hv) else: - logger.warning('Expected dict of vars for ' 'host "%s", got %s instead', hk, str(type(hv))) + logger.warning('Expected dict of vars for host "%s", got %s instead', hk, str(type(hv))) group.add_host(host) elif isinstance(hosts, (list, tuple)): for hk in hosts: @@ -262,13 +261,13 @@ def dict_to_mem_data(data, inventory=None): continue group.add_host(host) else: - logger.warning('Expected dict or list of "hosts" for ' 'group "%s", got %s instead', k, str(type(hosts))) + logger.warning('Expected dict or list of "hosts" for group "%s", got %s instead', k, str(type(hosts))) # Process group variables. vars = v.get('vars', {}) if isinstance(vars, dict): group.variables.update(vars) else: - logger.warning('Expected dict of vars for ' 'group "%s", got %s instead', k, str(type(vars))) + logger.warning('Expected dict of vars for group "%s", got %s instead', k, str(type(vars))) # Process child groups. children = v.get('children', []) if isinstance(children, (list, tuple)): @@ -277,7 +276,7 @@ def dict_to_mem_data(data, inventory=None): if child and c != 'ungrouped': group.add_child_group(child) else: - logger.warning('Expected list of children for ' 'group "%s", got %s instead', k, str(type(children))) + logger.warning('Expected list of children for group "%s", got %s instead', k, str(type(children))) # Load host names from a list. elif isinstance(v, (list, tuple)): @@ -288,7 +287,7 @@ def dict_to_mem_data(data, inventory=None): group.add_host(host) else: logger.warning('') - logger.warning('Expected dict or list for group "%s", ' 'got %s instead', k, str(type(v))) + logger.warning('Expected dict or list for group "%s", got %s instead', k, str(type(v))) if k not in ['all', 'ungrouped']: inventory.all_group.add_child_group(group) @@ -299,6 +298,6 @@ def dict_to_mem_data(data, inventory=None): if isinstance(meta_hostvars, dict): v.variables.update(meta_hostvars) else: - logger.warning('Expected dict of vars for ' 'host "%s", got %s instead', k, str(type(meta_hostvars))) + logger.warning('Expected dict of vars for host "%s", got %s instead', k, str(type(meta_hostvars))) return inventory diff --git a/awx/main/utils/migration.py b/awx/main/utils/migration.py new file mode 100644 index 000000000000..10396e7ea358 --- /dev/null +++ b/awx/main/utils/migration.py @@ -0,0 +1,14 @@ +from django.db.migrations.executor import MigrationExecutor +from django.db import connections, DEFAULT_DB_ALIAS + + +def is_database_synchronized(database=DEFAULT_DB_ALIAS): + """_summary_ + Ensure all migrations have ran + https://stackoverflow.com/questions/31838882/check-for-pending-django-migrations + """ + connection = connections[database] + connection.prepare_database() + executor = MigrationExecutor(connection) + targets = executor.loader.graph.leaf_nodes() + return not executor.migration_plan(targets) diff --git a/awx/main/utils/named_url_graph.py b/awx/main/utils/named_url_graph.py index 9d2c0a27c97d..51a85fc68441 100644 --- a/awx/main/utils/named_url_graph.py +++ b/awx/main/utils/named_url_graph.py @@ -5,8 +5,6 @@ # Django from django.db import models from django.conf import settings -from django.contrib.contenttypes.models import ContentType - NAMED_URL_RES_DILIMITER = "++" NAMED_URL_RES_INNER_DILIMITER = "+" @@ -245,6 +243,8 @@ def _generate_configurations(nodes): def _dfs(configuration, model, graph, dead_ends, new_deadends, parents): + from django.contrib.contenttypes.models import ContentType + parents.add(model) fields, fk_names = configuration[model][0][:], configuration[model][1][:] adj_list = [] @@ -306,3 +306,19 @@ def generate_graph(models): def reset_counters(): for node in settings.NAMED_URL_GRAPH.values(): node.counter = 0 + + +def _customize_graph(): + from django.contrib.auth.models import User + from awx.main.models import Instance, Schedule, UnifiedJobTemplate + + for model in [Schedule, UnifiedJobTemplate]: + if model in settings.NAMED_URL_GRAPH: + settings.NAMED_URL_GRAPH[model].remove_bindings() + settings.NAMED_URL_GRAPH.pop(model) + if User not in settings.NAMED_URL_GRAPH: + settings.NAMED_URL_GRAPH[User] = GraphNode(User, ['username'], []) + settings.NAMED_URL_GRAPH[User].add_bindings() + if Instance not in settings.NAMED_URL_GRAPH: + settings.NAMED_URL_GRAPH[Instance] = GraphNode(Instance, ['hostname'], []) + settings.NAMED_URL_GRAPH[Instance].add_bindings() diff --git a/awx/main/utils/pglock.py b/awx/main/utils/pglock.py deleted file mode 100644 index 3d8a00d20a57..000000000000 --- a/awx/main/utils/pglock.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017 Ansible by Red Hat -# All Rights Reserved. - -from contextlib import contextmanager - -from django_pglocks import advisory_lock as django_pglocks_advisory_lock -from django.db import connection - - -@contextmanager -def advisory_lock(*args, **kwargs): - if connection.vendor == 'postgresql': - with django_pglocks_advisory_lock(*args, **kwargs) as internal_lock: - yield internal_lock - else: - yield True diff --git a/awx/main/utils/plugins.py b/awx/main/utils/plugins.py new file mode 100644 index 000000000000..31fb2d3963b4 --- /dev/null +++ b/awx/main/utils/plugins.py @@ -0,0 +1,87 @@ +# Copyright (c) 2024 Ansible, Inc. +# All Rights Reserved. + +""" +This module contains the code responsible for extracting the lists of dynamically discovered plugins. +""" + +from functools import cache + + +@cache +def discover_available_cloud_provider_plugin_names() -> list[str]: + """ + Return a list of cloud plugin names available in runtime. + + The discovery result is cached since it does not change throughout + the life cycle of the server run. + + :returns: List of plugin cloud names. + :rtype: list[str] + """ + from awx.main.models.inventory import InventorySourceOptions + + plugin_names = list(InventorySourceOptions.injectors.keys()) + + plugin_names.remove('constructed') + + return plugin_names + + +@cache +def compute_cloud_inventory_sources() -> dict[str, str]: + """ + Return a dictionary of cloud provider plugin names + available plus source control management and constructed. + + :returns: Dictionary of plugin cloud names plus source control. + :rtype: dict[str, str] + """ + + plugins = discover_available_cloud_provider_plugin_names() + + return dict(zip(plugins, plugins), scm='scm', constructed='constructed') + + +@cache +def discover_available_cloud_provider_descriptions() -> dict[str, str]: + """ + Return a dictionary of cloud provider plugin descriptions + available. + + :returns: Dictionary of plugin cloud descriptions. + :rtype: dict[str, str] + """ + from awx.main.models.inventory import InventorySourceOptions + + plugin_description_list = [(plugin_name, plugin.plugin_description) for plugin_name, plugin in InventorySourceOptions.injectors.items()] + + plugin_description = dict(plugin_description_list) + + return plugin_description + + +@cache +def load_combined_inventory_source_options() -> dict[str, str]: + """ + Return a dictionary of cloud provider plugin names and 'file'. + + The 'file' entry is included separately since it needs to be consumed directly by the serializer. + + :returns: A dictionary of cloud provider plugin names (as both keys and values) plus the 'file' entry. + :rtype: dict[str, str] + """ + + plugins = compute_cloud_inventory_sources() + + plugin_description = discover_available_cloud_provider_descriptions() + + if 'scm' in plugins: + plugin_description['scm'] = 'Sourced from a Project' + + if 'file' in plugins: + plugin_description['file'] = 'File-based inventory source' + + result = {plugin: plugin_description.get(plugin, plugin) for plugin in plugins} + + return result diff --git a/awx/main/utils/proxy.py b/awx/main/utils/proxy.py new file mode 100644 index 000000000000..0676e8f44ae6 --- /dev/null +++ b/awx/main/utils/proxy.py @@ -0,0 +1,47 @@ +# Copyright (c) 2024 Ansible, Inc. +# All Rights Reserved. + + +# DRF +from rest_framework.request import Request + +""" +Note that these methods operate on request.environ. This data is from uwsgi. +It is the source data from which request.headers (read-only) is constructed. +""" + + +def is_proxy_in_headers(request: Request, proxy_list: list[str], headers: list[str]) -> bool: + """ + Determine if the request went through at least one proxy in the list. + Example: + request.environ = { + "HTTP_X_FOO": "8.8.8.8, 192.168.2.1", + "REMOTE_ADDR": "192.168.2.1", + "REMOTE_HOST": "foobar" + } + proxy_list = ["192.168.2.1"] + headers = ["HTTP_X_FOO", "REMOTE_ADDR", "REMOTE_HOST"] + + The above would return True since 192.168.2.1 is a value for the header HTTP_X_FOO + + request: The DRF/Django request. request.environ dict will be used for searching for proxies + proxy_list: A list of known and trusted proxies may be ip or hostnames + headers: A list of keys for which to consider values that may contain a proxy + """ + + remote_hosts = set() + + for header in headers: + for value in request.environ.get(header, '').split(','): + value = value.strip() + if value: + remote_hosts.add(value) + + return bool(remote_hosts.intersection(set(proxy_list))) + + +def delete_headers_starting_with_http(request: Request, headers: list[str]): + for header in headers: + if header.startswith('HTTP_'): + request.environ.pop(header, None) diff --git a/awx/main/utils/redis.py b/awx/main/utils/redis.py new file mode 100644 index 000000000000..98aa89ba29ce --- /dev/null +++ b/awx/main/utils/redis.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Ansible, Inc. +# All Rights Reserved + +"""Redis client utilities with automatic retry on connection errors.""" + +import redis +import redis.asyncio +from django.conf import settings +from redis.backoff import ExponentialBackoff +from redis.retry import Retry +from redis.exceptions import BusyLoadingError, ConnectionError, TimeoutError + + +def _get_redis_pool_kwargs(): + """ + Get common Redis connection pool kwargs with retry configuration. + + Returns: + dict: Keyword arguments for redis.ConnectionPool.from_url() + """ + retry = Retry(ExponentialBackoff(cap=settings.REDIS_BACKOFF_CAP, base=settings.REDIS_BACKOFF_BASE), retries=settings.REDIS_RETRY_COUNT) + return { + 'retry': retry, + 'retry_on_error': [BusyLoadingError, ConnectionError, TimeoutError], + } + + +def get_redis_client(): + """ + Create a Redis client with automatic retry on connection errors. + + This function creates a Redis connection with built-in retry logic to handle + transient connection failures (like broken pipes, timeouts, etc.) that can occur + during long-running operations. + + Based on PR feedback: https://github.com/ansible/awx/pull/16158#issuecomment-3486839154 + Uses redis-py's built-in retry mechanism instead of custom retry logic. + + Returns: + redis.Redis: A Redis client instance configured with retry logic + + Notes: + - Uses exponential backoff with configurable retries (REDIS_RETRY_COUNT setting) + - Retries on BusyLoadingError, ConnectionError, and TimeoutError + - Requires redis-py 7.0+ + """ + pool = redis.ConnectionPool.from_url( + settings.BROKER_URL, + **_get_redis_pool_kwargs(), + ) + return redis.Redis(connection_pool=pool) + + +def get_redis_client_async(): + """ + Create an async Redis client with automatic retry on connection errors. + + This is the async version of get_redis_client() for use with asyncio code. + + Returns: + redis.asyncio.Redis: An async Redis client instance configured with retry logic + + Notes: + - Uses exponential backoff with configurable retries (REDIS_RETRY_COUNT setting) + - Retries on BusyLoadingError, ConnectionError, and TimeoutError + - Requires redis-py 7.0+ + """ + pool = redis.asyncio.ConnectionPool.from_url( + settings.BROKER_URL, + **_get_redis_pool_kwargs(), + ) + return redis.asyncio.Redis(connection_pool=pool) diff --git a/awx/main/utils/reload.py b/awx/main/utils/reload.py index a7c2a1ed99b5..4306a1ae2fd3 100644 --- a/awx/main/utils/reload.py +++ b/awx/main/utils/reload.py @@ -6,7 +6,6 @@ import logging import os - logger = logging.getLogger('awx.main.utils.reload') @@ -17,7 +16,7 @@ def supervisor_service_command(command, service='*', communicate=True): """ args = ['supervisorctl'] - supervisor_config_path = os.getenv('SUPERVISOR_WEB_CONFIG_PATH', None) + supervisor_config_path = os.getenv('SUPERVISOR_CONFIG_PATH', None) if supervisor_config_path: args.extend(['-c', supervisor_config_path]) diff --git a/awx/main/utils/safe_yaml.py b/awx/main/utils/safe_yaml.py index abf21e3428db..3dbfffc6a177 100644 --- a/awx/main/utils/safe_yaml.py +++ b/awx/main/utils/safe_yaml.py @@ -1,7 +1,6 @@ import re import yaml - __all__ = ['safe_dump', 'SafeLoader'] diff --git a/awx/main/utils/update_model.py b/awx/main/utils/update_model.py index 0b2998561cdf..37f35f7091a0 100644 --- a/awx/main/utils/update_model.py +++ b/awx/main/utils/update_model.py @@ -6,7 +6,6 @@ from awx.main.tasks.signals import signal_callback - logger = logging.getLogger('awx.main.tasks.utils') diff --git a/awx/main/credential_plugins/__init__.py b/awx/main/utils/workload_identity.py similarity index 100% rename from awx/main/credential_plugins/__init__.py rename to awx/main/utils/workload_identity.py diff --git a/awx/main/validators.py b/awx/main/validators.py index 751d38060bbf..ad1552996b88 100644 --- a/awx/main/validators.py +++ b/awx/main/validators.py @@ -181,6 +181,8 @@ def validate_ssh_private_key(data): certificates; should handle any valid options for ssh_private_key on a credential. """ + # Strip leading and trailing whitespace/newlines to handle common copy-paste issues + data = data.strip() return validate_pem(data, min_keys=1) diff --git a/awx/main/wsbroadcast.py b/awx/main/wsbroadcast.py deleted file mode 100644 index 2c1f228785f2..000000000000 --- a/awx/main/wsbroadcast.py +++ /dev/null @@ -1,208 +0,0 @@ -import json -import logging -import asyncio - -import aiohttp -from aiohttp import client_exceptions -from asgiref.sync import sync_to_async - -from channels.layers import get_channel_layer - -from django.conf import settings -from django.apps import apps -from django.core.serializers.json import DjangoJSONEncoder - -from awx.main.analytics.broadcast_websocket import ( - BroadcastWebsocketStats, - BroadcastWebsocketStatsManager, -) -import awx.main.analytics.subsystem_metrics as s_metrics - -logger = logging.getLogger('awx.main.wsbroadcast') - - -def wrap_broadcast_msg(group, message: str): - # TODO: Maybe wrap as "group","message" so that we don't need to - # encode/decode as json. - return json.dumps(dict(group=group, message=message), cls=DjangoJSONEncoder) - - -def unwrap_broadcast_msg(payload: dict): - return (payload['group'], payload['message']) - - -@sync_to_async -def get_broadcast_hosts(): - Instance = apps.get_model('main', 'Instance') - instances = ( - Instance.objects.exclude(hostname=Instance.objects.my_hostname()) - .exclude(node_type='execution') - .exclude(node_type='hop') - .order_by('hostname') - .values('hostname', 'ip_address') - .distinct() - ) - return {i['hostname']: i['ip_address'] or i['hostname'] for i in instances} - - -def get_local_host(): - Instance = apps.get_model('main', 'Instance') - return Instance.objects.my_hostname() - - -class WebsocketTask: - def __init__( - self, - name, - event_loop, - stats: BroadcastWebsocketStats, - remote_host: str, - remote_port: int = settings.BROADCAST_WEBSOCKET_PORT, - protocol: str = settings.BROADCAST_WEBSOCKET_PROTOCOL, - verify_ssl: bool = settings.BROADCAST_WEBSOCKET_VERIFY_CERT, - endpoint: str = 'broadcast', - ): - self.name = name - self.event_loop = event_loop - self.stats = stats - self.remote_host = remote_host - self.remote_port = remote_port - self.endpoint = endpoint - self.protocol = protocol - self.verify_ssl = verify_ssl - self.channel_layer = None - self.subsystem_metrics = s_metrics.Metrics(instance_name=name) - - async def run_loop(self, websocket: aiohttp.ClientWebSocketResponse): - raise RuntimeError("Implement me") - - async def connect(self, attempt): - from awx.main.consumers import WebsocketSecretAuthHelper # noqa - - logger.debug(f"Connection from {self.name} to {self.remote_host} attempt number {attempt}.") - - ''' - Can not put get_channel_layer() in the init code because it is in the init - path of channel layers i.e. RedisChannelLayer() calls our init code. - ''' - if not self.channel_layer: - self.channel_layer = get_channel_layer() - - try: - if attempt > 0: - await asyncio.sleep(settings.BROADCAST_WEBSOCKET_RECONNECT_RETRY_RATE_SECONDS) - except asyncio.CancelledError: - logger.warning(f"Connection from {self.name} to {self.remote_host} cancelled") - raise - - uri = f"{self.protocol}://{self.remote_host}:{self.remote_port}/websocket/{self.endpoint}/" - timeout = aiohttp.ClientTimeout(total=10) - - secret_val = WebsocketSecretAuthHelper.construct_secret() - try: - async with aiohttp.ClientSession(headers={'secret': secret_val}, timeout=timeout) as session: - async with session.ws_connect(uri, ssl=self.verify_ssl, heartbeat=20) as websocket: - logger.info(f"Connection from {self.name} to {self.remote_host} established.") - self.stats.record_connection_established() - attempt = 0 - await self.run_loop(websocket) - except asyncio.CancelledError: - # TODO: Check if connected and disconnect - # Possibly use run_until_complete() if disconnect is async - logger.warning(f"Connection from {self.name} to {self.remote_host} cancelled.") - self.stats.record_connection_lost() - raise - except client_exceptions.ClientConnectorError as e: - logger.warning(f"Connection from {self.name} to {self.remote_host} failed: '{e}'.") - except asyncio.TimeoutError: - logger.warning(f"Connection from {self.name} to {self.remote_host} timed out.") - except Exception as e: - # Early on, this is our canary. I'm not sure what exceptions we can really encounter. - logger.exception(f"Connection from {self.name} to {self.remote_host} failed for unknown reason: '{e}'.") - else: - logger.warning(f"Connection from {self.name} to {self.remote_host} list.") - - self.stats.record_connection_lost() - self.start(attempt=attempt + 1) - - def start(self, attempt=0): - self.async_task = self.event_loop.create_task(self.connect(attempt=attempt)) - - def cancel(self): - self.async_task.cancel() - - -class BroadcastWebsocketTask(WebsocketTask): - async def run_loop(self, websocket: aiohttp.ClientWebSocketResponse): - async for msg in websocket: - self.stats.record_message_received() - - if msg.type == aiohttp.WSMsgType.ERROR: - break - elif msg.type == aiohttp.WSMsgType.TEXT: - try: - payload = json.loads(msg.data) - except json.JSONDecodeError: - logmsg = "Failed to decode broadcast message" - if logger.isEnabledFor(logging.DEBUG): - logmsg = "{} {}".format(logmsg, payload) - logger.warning(logmsg) - continue - (group, message) = unwrap_broadcast_msg(payload) - if group == "metrics": - self.subsystem_metrics.store_metrics(message) - continue - await self.channel_layer.group_send(group, {"type": "internal.message", "text": message}) - - -class BroadcastWebsocketManager(object): - def __init__(self): - self.event_loop = asyncio.get_event_loop() - ''' - { - 'hostname1': BroadcastWebsocketTask(), - 'hostname2': BroadcastWebsocketTask(), - 'hostname3': BroadcastWebsocketTask(), - } - ''' - self.broadcast_tasks = dict() - self.local_hostname = get_local_host() - self.stats_mgr = BroadcastWebsocketStatsManager(self.event_loop, self.local_hostname) - - async def run_per_host_websocket(self): - while True: - known_hosts = await get_broadcast_hosts() - future_remote_hosts = known_hosts.keys() - current_remote_hosts = self.broadcast_tasks.keys() - deleted_remote_hosts = set(current_remote_hosts) - set(future_remote_hosts) - new_remote_hosts = set(future_remote_hosts) - set(current_remote_hosts) - - remote_addresses = {k: v.remote_host for k, v in self.broadcast_tasks.items()} - for hostname, address in known_hosts.items(): - if hostname in self.broadcast_tasks and address != remote_addresses[hostname]: - deleted_remote_hosts.add(hostname) - new_remote_hosts.add(hostname) - - if deleted_remote_hosts: - logger.warning(f"Removing {deleted_remote_hosts} from websocket broadcast list") - if new_remote_hosts: - logger.warning(f"Adding {new_remote_hosts} to websocket broadcast list") - - for h in deleted_remote_hosts: - self.broadcast_tasks[h].cancel() - del self.broadcast_tasks[h] - self.stats_mgr.delete_remote_host_stats(h) - - for h in new_remote_hosts: - stats = self.stats_mgr.new_remote_host_stats(h) - broadcast_task = BroadcastWebsocketTask(name=self.local_hostname, event_loop=self.event_loop, stats=stats, remote_host=known_hosts[h]) - broadcast_task.start() - self.broadcast_tasks[h] = broadcast_task - - await asyncio.sleep(settings.BROADCAST_WEBSOCKET_NEW_INSTANCE_POLL_RATE_SECONDS) - - def start(self): - self.stats_mgr.start() - - self.async_task = self.event_loop.create_task(self.run_per_host_websocket()) - return self.async_task diff --git a/awx/main/wsrelay.py b/awx/main/wsrelay.py new file mode 100644 index 000000000000..d13ff136713a --- /dev/null +++ b/awx/main/wsrelay.py @@ -0,0 +1,376 @@ +import json +import logging +import asyncio +from typing import Dict +from copy import deepcopy + +import ipaddress + +import aiohttp +from aiohttp import client_exceptions +import redis + +from channels.layers import get_channel_layer + +from django.conf import settings +from django.apps import apps + +import psycopg + +from awx.main.analytics.broadcast_websocket import ( + RelayWebsocketStats, + RelayWebsocketStatsManager, +) + +logger = logging.getLogger('awx.main.wsrelay') + + +def wrap_broadcast_msg(group, message: str): + # TODO: Maybe wrap as "group","message" so that we don't need to + # encode/decode as json. + return dict(group=group, message=message) + + +def get_local_host(): + Instance = apps.get_model('main', 'Instance') + return Instance.objects.my_hostname() + + +class WebsocketRelayConnection: + def __init__( + self, + name, + stats: RelayWebsocketStats, + remote_host: str, + remote_port: int = settings.BROADCAST_WEBSOCKET_PORT, + protocol: str = settings.BROADCAST_WEBSOCKET_PROTOCOL, + verify_ssl: bool = settings.BROADCAST_WEBSOCKET_VERIFY_CERT, + ): + self.name = name + self.stats = stats + self.remote_host = remote_host + self.remote_port = remote_port + self.protocol = protocol + self.verify_ssl = verify_ssl + self.channel_layer = None + self.producers = dict() + self.connected = False + + async def run_loop(self, websocket: aiohttp.ClientWebSocketResponse): + raise RuntimeError("Implement me") + + async def connect(self): + from awx.main.consumers import WebsocketSecretAuthHelper # noqa + + logger.debug(f"Connection attempt from {self.name} to {self.remote_host}") + + ''' + Can not put get_channel_layer() in the init code because it is in the init + path of channel layers i.e. RedisChannelLayer() calls our init code. + ''' + if not self.channel_layer: + self.channel_layer = get_channel_layer() + + # figure out if what we have is an ipaddress, IPv6 Addresses must have brackets added for uri + uri_hostname = self.remote_host + try: + # Throws ValueError if self.remote_host is a hostname like example.com, not an IPv4 or IPv6 ip address + if isinstance(ipaddress.ip_address(uri_hostname), ipaddress.IPv6Address): + uri_hostname = f"[{uri_hostname}]" + except ValueError: + pass + + uri = f"{self.protocol}://{uri_hostname}:{self.remote_port}/websocket/relay/" + timeout = aiohttp.ClientTimeout(total=10) + + secret_val = WebsocketSecretAuthHelper.construct_secret() + try: + async with aiohttp.ClientSession(headers={'secret': secret_val}, timeout=timeout) as session: + async with session.ws_connect(uri, ssl=self.verify_ssl, heartbeat=20) as websocket: + logger.info(f"Connection from {self.name} to {self.remote_host} established.") + self.stats.record_connection_established() + self.connected = True + await self.run_connection(websocket) + except asyncio.CancelledError: + # TODO: Check if connected and disconnect + # Possibly use run_until_complete() if disconnect is async + logger.warning(f"Connection from {self.name} to {self.remote_host} canceled.") + except client_exceptions.ClientConnectorError as e: + logger.warning(f"Connection from {self.name} to {self.remote_host} failed: '{e}'.", exc_info=True) + except asyncio.TimeoutError: + logger.warning(f"Connection from {self.name} to {self.remote_host} timed out.") + except Exception as e: + # Early on, this is our canary. I'm not sure what exceptions we can really encounter. + logger.warning(f"Connection from {self.name} to {self.remote_host} failed for unknown reason: '{e}'.", exc_info=True) + else: + logger.debug(f"Connection from {self.name} to {self.remote_host} lost, but no exception was raised.") + finally: + self.connected = False + self.stats.record_connection_lost() + + def start(self): + self.async_task = asyncio.get_running_loop().create_task( + self.connect(), + name=f"WebsocketRelayConnection.connect.{self.name}", + ) + return self.async_task + + def cancel(self): + self.async_task.cancel() + + async def run_connection(self, websocket: aiohttp.ClientWebSocketResponse): + # create a dedicated subsystem metric producer to handle local subsystem + # metrics messages + # the "metrics" group is not subscribed to in the typical fashion, so we + # just explicitly create it + producer = asyncio.get_running_loop().create_task( + self.run_producer("metrics", websocket, "metrics"), + name="WebsocketRelayConnection.run_producer.metrics", + ) + self.producers["metrics"] = {"task": producer, "subscriptions": {"metrics"}} + async for msg in websocket: + self.stats.record_message_received() + + if msg.type == aiohttp.WSMsgType.ERROR: + break + elif msg.type == aiohttp.WSMsgType.TEXT: + try: + payload = json.loads(msg.data) + except json.JSONDecodeError: + logmsg = "Failed to decode message from web node" + if logger.isEnabledFor(logging.DEBUG): + logmsg = "{} {}".format(logmsg, msg.data) + logger.warning(logmsg) + continue + + if payload.get("type") == "consumer.subscribe": + for group in payload['groups']: + name = f"{self.remote_host}-{group}" + origin_channel = payload['origin_channel'] + if not self.producers.get(name): + producer = asyncio.get_running_loop().create_task( + self.run_producer(name, websocket, group), + name=f"WebsocketRelayConnection.run_producer.{name}", + ) + self.producers[name] = {"task": producer, "subscriptions": {origin_channel}} + logger.debug(f"Producer {name} started.") + else: + self.producers[name]["subscriptions"].add(origin_channel) + logger.debug(f"Connection from {self.name} to {self.remote_host} added subscription to {group}.") + + if payload.get("type") == "consumer.unsubscribe": + for group in payload['groups']: + name = f"{self.remote_host}-{group}" + origin_channel = payload['origin_channel'] + try: + self.producers[name]["subscriptions"].remove(origin_channel) + logger.debug(f"Unsubscribed {origin_channel} from {name}") + except KeyError: + logger.warning(f"Producer {name} not found.") + + async def run_producer(self, name, websocket, group): + try: + logger.info(f"Starting producer for {name}") + + consumer_channel = await self.channel_layer.new_channel() + await self.channel_layer.group_add(group, consumer_channel) + logger.debug(f"Producer {name} added to group {group} and is now awaiting messages.") + + while True: + try: + msg = await asyncio.wait_for(self.channel_layer.receive(consumer_channel), timeout=10) + if not msg.get("needs_relay"): + # This is added in by emit_channel_notification(). It prevents us from looping + # in the event that we are sharing a redis with a web instance. We'll see the + # message once (it'll have needs_relay=True), we'll delete that, and then forward + # the message along. The web instance will add it back to the same channels group, + # but it won't have needs_relay=True, so we'll ignore it. + continue + + # We need to copy the message because we're going to delete the needs_relay key + # and we don't want to modify the original message because other producers may + # still need to act on it. It seems weird, but it's necessary. + msg = dict(msg) + del msg["needs_relay"] + except asyncio.TimeoutError: + current_subscriptions = self.producers[name]["subscriptions"] + if len(current_subscriptions) == 0: + logger.info(f"Producer {name} has no subscribers, shutting down.") + return + + continue + except redis.exceptions.ConnectionError: + logger.info(f"Producer {name} lost connection to Redis, shutting down.") + return + + await websocket.send_json(wrap_broadcast_msg(group, msg)) + except ConnectionResetError: + # This can be hit when a web node is scaling down and we try to write to it. + # There's really nothing to do in this case and it's a fairly typical thing to happen. + # We'll log it as debug, but it's not really a problem. + logger.debug(f"Producer {name} connection reset.") + pass + except Exception: + # Note, this is very intentional and important since we do not otherwise + # ever check the result of this future. Without this line you will not see an error if + # something goes wrong in here. + logger.exception(f"Event relay producer {name} crashed") + finally: + await self.channel_layer.group_discard(group, consumer_channel) + del self.producers[name] + + +class WebSocketRelayManager(object): + def __init__(self): + self.local_hostname = get_local_host() + self.relay_connections = dict() + # hostname -> ip + self.known_hosts: Dict[str, str] = dict() + + async def on_ws_heartbeat(self, conn): + await conn.execute("LISTEN web_ws_heartbeat") + async for notif in conn.notifies(): + if notif is None: + continue + try: + if not notif.payload or notif.channel != "web_ws_heartbeat": + logger.warning(f"Unexpected channel or missing payload. {notif.channel}, {notif.payload}") + continue + + try: + payload = json.loads(notif.payload) + except json.JSONDecodeError: + logmsg = "Failed to decode message from pg_notify channel `web_ws_heartbeat`" + if logger.isEnabledFor(logging.DEBUG): + logmsg = "{} {}".format(logmsg, notif.payload) + logger.warning(logmsg) + continue + + # Skip if the message comes from the same host we are running on + # In this case, we'll be sharing a redis, no need to relay. + if payload.get("hostname") == self.local_hostname: + hostname = payload.get("hostname") + logger.debug(f"Received a heartbeat request for {hostname}. Skipping as we use redis for local host.") + continue + + action = payload.get("action") + + if action in ("online", "offline"): + hostname = payload.get("hostname") + ip = payload.get("ip") or hostname # try back to hostname if ip isn't supplied + if ip is None: + logger.warning(f"Received invalid {action} ws_heartbeat, missing hostname and ip: {payload}") + continue + logger.debug(f"Web host {hostname} ({ip}) {action} heartbeat received.") + + if action == "online": + self.known_hosts[hostname] = ip + elif action == "offline": + await self.cleanup_offline_host(hostname) + except Exception as e: + # This catch-all is the same as the one above. asyncio will eat the exception + # but we want to know about it. + logger.exception(f"on_ws_heartbeat exception: {e}") + + async def cleanup_offline_host(self, hostname): + """ + Given a hostname, try to cancel its task/connection and remove it from + the list of hosts we know about. + If the host isn't in the list, assume that it was already deleted and + don't error. + """ + if hostname in self.relay_connections: + self.relay_connections[hostname].cancel() + + # Wait for the task to actually run its cancel/completion logic + # otherwise it might get GC'd too early when we del it below. + # Being GC'd too early could generate a scary message in logs: + # "Task was destroyed but it is pending!" + try: + await asyncio.wait_for(self.relay_connections[hostname].async_task, timeout=10) + except asyncio.TimeoutError: + logger.warning(f"Tried to cancel relay connection for {hostname} but it timed out during cleanup.") + except asyncio.CancelledError: + # Handle the case where the task was already canceled by the time we got here. + pass + + del self.relay_connections[hostname] + + if hostname in self.known_hosts: + del self.known_hosts[hostname] + + try: + self.stats_mgr.delete_remote_host_stats(hostname) + except KeyError: + pass + + async def run(self): + self.stats_mgr = RelayWebsocketStatsManager(self.local_hostname) + self.stats_mgr.start() + + database_conf = deepcopy(settings.DATABASES['default']) + database_conf['OPTIONS'] = deepcopy(database_conf.get('OPTIONS', {})) + + for k, v in settings.LISTENER_DATABASES.get('default', {}).items(): + if k != 'OPTIONS': + database_conf[k] = v + for k, v in settings.LISTENER_DATABASES.get('default', {}).get('OPTIONS', {}).items(): + database_conf['OPTIONS'][k] = v + + if 'PASSWORD' in database_conf: + database_conf['OPTIONS']['password'] = database_conf.pop('PASSWORD') + + async_conn = await psycopg.AsyncConnection.connect( + dbname=database_conf['NAME'], + host=database_conf['HOST'], + user=database_conf['USER'], + port=database_conf['PORT'], + **database_conf.get("OPTIONS", {}), + ) + + await async_conn.set_autocommit(True) + on_ws_heartbeat_task = asyncio.get_running_loop().create_task( + self.on_ws_heartbeat(async_conn), + name="WebSocketRelayManager.on_ws_heartbeat", + ) + + # Establishes a websocket connection to /websocket/relay on all API servers + while True: + if on_ws_heartbeat_task.done(): + raise Exception("on_ws_heartbeat_task has exited") + + future_remote_hosts = self.known_hosts.keys() + current_remote_hosts = self.relay_connections.keys() + deleted_remote_hosts = set(current_remote_hosts) - set(future_remote_hosts) + new_remote_hosts = set(future_remote_hosts) - set(current_remote_hosts) + + # This loop handles if we get an advertisement from a host we already know about but + # the advertisement has a different IP than we are currently connected to. + for hostname, address in self.known_hosts.items(): + if hostname not in self.relay_connections: + # We've picked up a new hostname that we don't know about yet. + continue + + if address != self.relay_connections[hostname].remote_host: + deleted_remote_hosts.add(hostname) + new_remote_hosts.add(hostname) + + # Delete any hosts with closed connections + for hostname, relay_conn in self.relay_connections.items(): + if not relay_conn.connected: + deleted_remote_hosts.add(hostname) + + if deleted_remote_hosts: + logger.info(f"Removing {deleted_remote_hosts} from websocket broadcast list") + await asyncio.gather(*[self.cleanup_offline_host(h) for h in deleted_remote_hosts]) + + if new_remote_hosts: + logger.info(f"Adding {new_remote_hosts} to websocket broadcast list") + + for h in new_remote_hosts: + stats = self.stats_mgr.new_remote_host_stats(h) + relay_connection = WebsocketRelayConnection(name=self.local_hostname, stats=stats, remote_host=self.known_hosts[h]) + relay_connection.start() + self.relay_connections[h] = relay_connection + + await asyncio.sleep(settings.BROADCAST_WEBSOCKET_NEW_INSTANCE_POLL_RATE_SECONDS) diff --git a/awx/playbooks/action_plugins/insights.py b/awx/playbooks/action_plugins/insights.py index 0d9bf6abd7ff..2d6b563c292f 100644 --- a/awx/playbooks/action_plugins/insights.py +++ b/awx/playbooks/action_plugins/insights.py @@ -9,6 +9,8 @@ from ansible.plugins.action import ActionBase +DEFAULT_OIDC_ENDPOINT = 'https://sso.redhat.com/auth/realms/redhat-external' + class ActionModule(ActionBase): def save_playbook(self, proj_path, remediation, content): @@ -34,31 +36,78 @@ def write_version(self, proj_path, etag): with open(file_path, 'w') as f: f.write(etag) + def _obtain_auth_token(self, oidc_endpoint, client_id, client_secret): + if oidc_endpoint.endswith('/'): + oidc_endpoint = oidc_endpoint[:-1] + main_url = oidc_endpoint + '/.well-known/openid-configuration' + response = requests.get(url=main_url, headers={'Accept': 'application/json'}) + data = {} + if response.status_code != 200: + data['failed'] = True + data['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format( + main_url, response.status_code, response.content + ) + return data + + auth_url = response.json().get('token_endpoint', None) + data = { + 'grant_type': 'client_credentials', + 'scope': 'api.console', + 'client_id': client_id, + 'client_secret': client_secret, + } + response = requests.post(url=auth_url, data=data) + + if response.status_code != 200: + data['failed'] = True + data['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format( + auth_url, response.status_code, response.content + ) + else: + data['token'] = response.json().get('access_token', None) + data['token_type'] = response.json().get('token_type', None) + return data + def run(self, tmp=None, task_vars=None): self._supports_check_mode = False + session = requests.Session() result = super(ActionModule, self).run(tmp, task_vars) insights_url = self._task.args.get('insights_url', None) - username = self._task.args.get('username', None) - password = self._task.args.get('password', None) proj_path = self._task.args.get('project_path', None) license = self._task.args.get('awx_license_type', None) awx_version = self._task.args.get('awx_version', None) + authentication = self._task.args.get('authentication', None) + username = self._task.args.get('username', None) + password = self._task.args.get('password', None) + client_id = self._task.args.get('client_id', None) + client_secret = self._task.args.get('client_secret', None) + + session.headers.update( + { + 'Content-Type': 'application/json', + 'User-Agent': '{} {} ({})'.format('AWX' if license == 'open' else 'Red Hat Ansible Automation Platform', awx_version, license), + } + ) + + if authentication == 'service_account' or (client_id and client_secret): + data = self._obtain_auth_token(DEFAULT_OIDC_ENDPOINT, client_id, client_secret) + if 'token' not in data: + result['failed'] = data['failed'] + result['msg'] = data['msg'] + return result + session.headers.update({'Authorization': f'{data["token_type"]} {data["token"]}'}) + elif authentication == 'basic' or (username and password): + session.auth = requests.auth.HTTPBasicAuth(username, password) - session = requests.Session() - session.auth = requests.auth.HTTPBasicAuth(username, password) - headers = { - 'Content-Type': 'application/json', - 'User-Agent': '{} {} ({})'.format('AWX' if license == 'open' else 'Red Hat Ansible Automation Platform', awx_version, license), - } url = '/api/remediations/v1/remediations' while url: - res = session.get('{}{}'.format(insights_url, url), headers=headers, timeout=120) + res = session.get('{}{}'.format(insights_url, url), timeout=120) if res.status_code != 200: result['failed'] = True - result['msg'] = 'Expected {} to return a status code of 200 but returned status ' 'code "{}" instead with content "{}".'.format( + result['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format( url, res.status_code, res.content ) return result @@ -87,7 +136,7 @@ def run(self, tmp=None, task_vars=None): continue elif res.status_code != 200: result['failed'] = True - result['msg'] = 'Expected {} to return a status code of 200 but returned status ' 'code "{}" instead with content "{}".'.format( + result['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format( playbook_url, res.status_code, res.content ) return result diff --git a/awx/playbooks/library/indirect_instance_count.py b/awx/playbooks/library/indirect_instance_count.py new file mode 100644 index 000000000000..4973f76c0534 --- /dev/null +++ b/awx/playbooks/library/indirect_instance_count.py @@ -0,0 +1,203 @@ +# (C) 2012, Michael DeHaan, +# (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = ''' + callback: host_query + type: notification + short_description: for demo of indirect host data and counting, this produces collection data + version_added: historical + description: + - Saves collection data to artifacts folder + requirements: + - Whitelist in configuration + - Set AWX_ISOLATED_DATA_DIR, AWX will do this +''' + +import os +import json +import re +from importlib.resources import files + +from packaging.version import Version, InvalidVersion + +from ansible.plugins.callback import CallbackBase + +# NOTE: in Ansible 1.2 or later general logging is available without +# this plugin, just set ANSIBLE_LOG_PATH as an environment variable +# or log_path in the DEFAULTS section of your ansible configuration +# file. This callback is an example of per hosts logging for those +# that want it. + + +# Taken from https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/galaxy.py#L1624 + +from ansible.cli.galaxy import with_collection_artifacts_manager +from ansible.release import __version__ + +from ansible.galaxy.collection import find_existing_collections +from ansible.utils.collection_loader import AnsibleCollectionConfig +import ansible.constants as C + +# External query path constants +EXTERNAL_QUERY_COLLECTION = 'ansible_collections.redhat.indirect_accounting' + + +def _get_query_file_dir(): + """Return the query file directory or None.""" + try: + queries_dir = files(EXTERNAL_QUERY_COLLECTION) / 'extensions' / 'audit' / 'external_queries' + except ModuleNotFoundError: + return None + if not queries_dir.is_dir(): + return None + return queries_dir + + +def list_external_queries(namespace, name): + """List all available external query versions for a collection. + + Args: + namespace: Collection namespace (e.g., 'community') + name: Collection name (e.g., 'vmware') + + Returns: + List of Version objects for all available query files + matching the namespace.name pattern. + """ + versions = [] + + if not (queries_dir := _get_query_file_dir()): + return versions + + # Pattern: namespace.name.X.Y.Z.yml where X.Y.Z is the version + pattern = re.compile(rf'^{re.escape(namespace)}\.{re.escape(name)}\.(.+)\.yml$') + + for query_file in queries_dir.iterdir(): + match = pattern.match(query_file.name) + if match: + version_str = match.group(1) + try: + versions.append(Version(version_str)) + except InvalidVersion: + # Skip files with invalid version strings + pass + + return versions + + +def find_external_query_with_fallback(namespace, name, installed_version): + """Find external query file with semantic version fallback. + + Args: + namespace: Collection namespace (e.g., 'community') + name: Collection name (e.g., 'vmware') + installed_version: Version string of installed collection (e.g., '4.5.0') + + Returns: + Tuple of (query_content, fallback_used, fallback_version) or (None, False, None) + - query_content: The query file content if found + - fallback_used: True if a fallback version was used instead of exact match + - fallback_version: The version string used (for logging) + """ + if not (queries_dir := _get_query_file_dir()): + return None, False, None + + # 1. Try exact version match first + exact_file = queries_dir / f'{namespace}.{name}.{installed_version}.yml' + if exact_file.exists(): + with exact_file.open('r') as f: + return f.read(), False, installed_version + + # 2. Find compatible fallback (same major version, nearest lower version) + try: + installed_version_object = Version(installed_version) + except InvalidVersion: + # Can't do version comparison for fallback + return None, False, None + available_versions = list_external_queries(namespace, name) + if not available_versions: + return None, False, None + + # Filter to same major version and versions <= installed version + compatible_versions = [v for v in available_versions if v.major == installed_version_object.major and v <= installed_version_object] + if not compatible_versions: + return None, False, None + + # Select nearest lower version - highest compatible version + fallback_version_object = max(compatible_versions) + fallback_version_str = str(fallback_version_object) + fallback_file = queries_dir / f'{namespace}.{name}.{fallback_version_str}.yml' + if fallback_file.exists(): + with fallback_file.open('r') as f: + return f.read(), True, fallback_version_str + + return None, False, None + + +@with_collection_artifacts_manager +def list_collections(artifacts_manager=None): + artifacts_manager.require_build_metadata = False + + default_collections_path = set(C.COLLECTIONS_PATHS) + collections_search_paths = default_collections_path | set(AnsibleCollectionConfig.collection_paths) + collections = list(find_existing_collections(list(collections_search_paths), artifacts_manager, dedupe=False)) + return collections + + +class CallbackModule(CallbackBase): + """ + logs playbook results, per host, in /var/log/ansible/hosts + """ + + CALLBACK_VERSION = 2.0 + CALLBACK_TYPE = 'notification' + CALLBACK_NAME = 'indirect_instance_count' + CALLBACK_NEEDS_WHITELIST = True + + TIME_FORMAT = "%b %d %Y %H:%M:%S" + MSG_FORMAT = "%(now)s - %(category)s - %(data)s\n\n" + + def v2_playbook_on_stats(self, stats): + artifact_dir = os.getenv('AWX_ISOLATED_DATA_DIR') + if not artifact_dir: + raise RuntimeError('Only suitable in AWX, did not find private_data_dir') + + collections_print = {} + # Loop over collections, from ansible-core these are Candidate objects + for candidate in list_collections(): + collection_print = { + 'version': candidate.ver, + } + + # 1. Check for embedded query file (takes precedence) + embedded_query_file = files(f'ansible_collections.{candidate.namespace}.{candidate.name}') / 'extensions' / 'audit' / 'event_query.yml' + if embedded_query_file.exists(): + with embedded_query_file.open('r') as f: + collection_print['host_query'] = f.read() + self._display.vv(f"Using embedded query for {candidate.fqcn} v{candidate.ver}") + else: + # 2. Check for external query file with version fallback + query_content, fallback_used, version_used = find_external_query_with_fallback(candidate.namespace, candidate.name, candidate.ver) + if query_content: + collection_print['host_query'] = query_content + if fallback_used: + # AC5.6: Log when fallback is used + self._display.v(f"Using external query {version_used} for {candidate.fqcn} v{candidate.ver}.") + else: + self._display.v(f"Using external query for {candidate.fqcn} v{candidate.ver}") + + collections_print[candidate.fqcn] = collection_print + + ansible_data = {'installed_collections': collections_print, 'ansible_version': __version__} + + write_path = os.path.join(artifact_dir, 'ansible_data.json') + with open(write_path, "w") as fd: + fd.write(json.dumps(ansible_data, indent=2)) + + super().v2_playbook_on_stats(stats) diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 2067e76043cb..22db2a7152c6 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -19,48 +19,58 @@ # awx_version: Current running version of the awx or tower as a string # awx_license_type: "open" for AWX; else presume Tower # gpg_pubkey: the GPG public key to use for validation, when enabled +# client_id: Red Hat service account client ID; required for the 'service_account' authentication method used against the Insights API +# client_secret: Red Hat service account client secret; required for the 'service_account' authentication method used against the Insights API +# authentication: The authentication method to use against the Insights API +# client_id and client_secret are required for the 'service_account' authentication method +# scm_username and scm_password are required for the 'basic' authentication method - hosts: localhost gather_facts: false connection: local name: Update source tree if necessary tasks: - - - name: delete project directory before update - command: "find -delete" # volume mounted, cannot delete folder itself + - name: Delete project directory before update + ansible.builtin.shell: set -o pipefail && find . -delete -print | tail -2 # volume mounted, cannot delete folder itself + register: reg + changed_when: reg.stdout_lines | length > 1 args: chdir: "{{ project_path }}" tags: - delete - - block: - - name: update project using git - git: - dest: "{{project_path|quote}}" - repo: "{{scm_url}}" - version: "{{scm_branch|quote}}" - refspec: "{{scm_refspec|default(omit)}}" - force: "{{scm_clean}}" - track_submodules: "{{scm_track_submodules|default(omit)}}" - accept_hostkey: "{{scm_accept_hostkey|default(omit)}}" + - name: Update project using git + tags: + - update_git + block: + - name: Update project using git + ansible.builtin.git: + dest: "{{ project_path | quote }}" + repo: "{{ scm_url }}" + version: "{{ scm_branch | quote }}" + refspec: "{{ scm_refspec | default(omit) }}" + force: "{{ scm_clean }}" + track_submodules: "{{ scm_track_submodules | default(omit) }}" + accept_hostkey: "{{ scm_accept_hostkey | default(omit) }}" register: git_result - name: Set the git repository version - set_fact: + ansible.builtin.set_fact: scm_version: "{{ git_result['after'] }}" when: "'after' in git_result" - tags: - - update_git - - block: - - name: update project using svn - subversion: - dest: "{{project_path|quote}}" - repo: "{{scm_url|quote}}" - revision: "{{scm_branch|quote}}" - force: "{{scm_clean}}" - username: "{{scm_username|default(omit)}}" - password: "{{scm_password|default(omit)}}" + - name: Update project using svn + tags: + - update_svn + block: + - name: Update project using svn + ansible.builtin.subversion: + dest: "{{ project_path | quote }}" + repo: "{{ scm_url | quote }}" + revision: "{{ scm_branch | quote }}" + force: "{{ scm_clean }}" + username: "{{ scm_username | default(omit) }}" + password: "{{ scm_password | default(omit) }}" # must be in_place because folder pre-existing, because it is mounted in_place: true environment: @@ -68,85 +78,93 @@ register: svn_result - name: Set the svn repository version - set_fact: + ansible.builtin.set_fact: scm_version: "{{ svn_result['after'] }}" when: "'after' in svn_result" - - name: parse subversion version string properly - set_fact: - scm_version: "{{scm_version|regex_replace('^.*Revision: ([0-9]+).*$', '\\1')}}" - tags: - - update_svn + - name: Parse subversion version string properly + ansible.builtin.set_fact: + scm_version: "{{ scm_version | regex_replace('^.*Revision: ([0-9]+).*$', '\\1') }}" - - block: + + - name: Project update for Insights + tags: + - update_insights + block: - name: Ensure the project directory is present - file: - dest: "{{project_path|quote}}" + ansible.builtin.file: + dest: "{{ project_path | quote }}" state: directory + mode: '0755' - name: Fetch Insights Playbook(s) insights: - insights_url: "{{insights_url}}" - username: "{{scm_username}}" - password: "{{scm_password}}" - project_path: "{{project_path}}" - awx_license_type: "{{awx_license_type}}" - awx_version: "{{awx_version}}" + insights_url: "{{ insights_url }}" + username: "{{ scm_username | default(omit) }}" + password: "{{ scm_password | default(omit) }}" + project_path: "{{ project_path }}" + awx_license_type: "{{ awx_license_type }}" + awx_version: "{{ awx_version }}" + client_id: "{{ client_id | default(omit) }}" + client_secret: "{{ client_secret | default(omit) }}" + authentication: "{{ authentication | default(omit) }}" register: results - name: Save Insights Version - set_fact: - scm_version: "{{results.version}}" + ansible.builtin.set_fact: + scm_version: "{{ results.version }}" when: results is defined - tags: - - update_insights - - block: + + - name: Update project using archive + tags: + - update_archive + block: - name: Ensure the project archive directory is present - file: - dest: "{{ project_path|quote }}/.archive" + ansible.builtin.file: + dest: "{{ project_path | quote }}/.archive" state: directory + mode: '0755' - name: Get archive from url - get_url: - url: "{{ scm_url|quote }}" - dest: "{{ project_path|quote }}/.archive/" - url_username: "{{ scm_username|default(omit) }}" - url_password: "{{ scm_password|default(omit) }}" + ansible.builtin.get_url: + url: "{{ scm_url | quote }}" + dest: "{{ project_path | quote }}/.archive/" + url_username: "{{ scm_username | default(omit) }}" + url_password: "{{ scm_password | default(omit) }}" force_basic_auth: true + mode: '0755' register: get_archive - name: Unpack archive project_archive: src: "{{ get_archive.dest }}" - project_path: "{{ project_path|quote }}" + project_path: "{{ project_path | quote }}" force: "{{ scm_clean }}" when: get_archive.changed or scm_clean register: unarchived - name: Find previous archives - find: - paths: "{{ project_path|quote }}/.archive/" + ansible.builtin.find: + paths: "{{ project_path | quote }}/.archive/" excludes: - - "{{ get_archive.dest|basename }}" + - "{{ get_archive.dest | basename }}" when: unarchived.changed register: previous_archive - name: Remove previous archives - file: + ansible.builtin.file: path: "{{ item.path }}" state: absent loop: "{{ previous_archive.files }}" - when: previous_archive.files|default([]) + when: previous_archive.files | default([]) - name: Set scm_version to archive sha1 checksum - set_fact: + ansible.builtin.set_fact: scm_version: "{{ get_archive.checksum_src }}" - tags: - - update_archive - name: Repository Version - debug: + ansible.builtin.debug: msg: "Repository Version {{ scm_version }}" tags: - update_git @@ -179,64 +197,94 @@ connection: local name: Install content with ansible-galaxy command if necessary vars: - galaxy_task_env: # configure in settings - additional_collections_env: - # These environment variables are used for installing collections, in addition to galaxy_task_env - # setting the collections paths silences warnings - ANSIBLE_COLLECTIONS_PATHS: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections" + galaxy_task_env: # configured in settings + # additional_galaxy_env contains environment variables are used for installing roles and collections and will take precedence over items in galaxy_task_env + additional_galaxy_env: + # These paths control where ansible-galaxy installs collections and roles on top the filesystem + ANSIBLE_COLLECTIONS_PATH: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections" + ANSIBLE_ROLES_PATH: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_roles" # Put the local tmp directory in same volume as collection destination # otherwise, files cannot be moved accross volumes and will cause error - ANSIBLE_LOCAL_TEMP: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/tmp" + ANSIBLE_LOCAL_TEMP: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/tmp" tasks: - - name: Check content sync settings + when: not roles_enabled | bool and not collections_enabled | bool + tags: + - install_roles + - install_collections block: - - debug: + - name: Warn about disabled content sync + ansible.builtin.debug: msg: > Collection and role syncing disabled. Check the AWX_ROLES_ENABLED and AWX_COLLECTIONS_ENABLED settings and Galaxy credentials on the project's organization. + - name: End play due to disabled content sync + ansible.builtin.meta: end_play - - meta: end_play + - block: + - name: Fetch galaxy roles from roles/requirements.(yml/yaml) + ansible.builtin.command: + cmd: "ansible-galaxy role install -r {{ req_file }} {{ verbosity }}" + register: galaxy_result + vars: + req_file: "{{ lookup('ansible.builtin.first_found', req_candidates, skip=True) }}" + req_candidates: + files: + - "{{ project_path | quote }}/roles/requirements.yml" + - "{{ project_path | quote }}/roles/requirements.yaml" + skip: True + changed_when: "'was installed successfully' in galaxy_result.stdout" + when: + - roles_enabled | bool + - req_file | length > 0 + tags: + - install_roles - when: not roles_enabled|bool and not collections_enabled|bool - tags: - - install_roles - - install_collections + - name: Fetch galaxy collections from collections/requirements.(yml/yaml) + ansible.builtin.command: + cmd: "ansible-galaxy collection install -r {{ req_file }} {{ verbosity }}" + register: galaxy_collection_result + vars: + req_file: "{{ lookup('ansible.builtin.first_found', req_candidates, skip=True) }}" + req_candidates: + files: + - "{{ project_path | quote }}/collections/requirements.yml" + - "{{ project_path | quote }}/collections/requirements.yaml" + skip: True + changed_when: "'Nothing to do.' not in galaxy_collection_result.stdout" + when: + - "ansible_version.full is version_compare('2.9', '>=')" + - collections_enabled | bool + - req_file | length > 0 + tags: + - install_collections - - name: fetch galaxy roles from requirements.(yml/yaml) - command: > - ansible-galaxy role install -r {{ item }} - --roles-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_roles - {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} - args: - chdir: "{{project_path|quote}}" - register: galaxy_result - with_fileglob: - - "{{project_path|quote}}/roles/requirements.yaml" - - "{{project_path|quote}}/roles/requirements.yml" - changed_when: "'was installed successfully' in galaxy_result.stdout" - environment: "{{ galaxy_task_env }}" - when: roles_enabled|bool - tags: - - install_roles + # requirements.yml in project root can be either "old" (roles only) or "new" (collections+roles) format + - name: Fetch galaxy roles and collections from requirements.(yml/yaml) + ansible.builtin.command: + cmd: "ansible-galaxy install -r {{ req_file }} {{ verbosity }}" + register: galaxy_combined_result + vars: + req_file: "{{ lookup('ansible.builtin.first_found', req_candidates, skip=True) }}" + req_candidates: + files: + - "{{ project_path | quote }}/requirements.yaml" + - "{{ project_path | quote }}/requirements.yml" + skip: True + changed_when: "'Nothing to do.' not in galaxy_combined_result.stdout" + when: + - "ansible_version.full is version_compare('2.10', '>=')" + - collections_enabled | bool + - roles_enabled | bool + - req_file | length > 0 + tags: + - install_collections + - install_roles + module_defaults: + ansible.builtin.command: + chdir: "{{ project_path | quote }}" - - name: fetch galaxy collections from collections/requirements.(yml/yaml) - command: > - ansible-galaxy collection install -r {{ item }} - --collections-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections - {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} - args: - chdir: "{{project_path|quote}}" - register: galaxy_collection_result - with_fileglob: - - "{{project_path|quote}}/collections/requirements.yaml" - - "{{project_path|quote}}/collections/requirements.yml" - - "{{project_path|quote}}/requirements.yaml" - - "{{project_path|quote}}/requirements.yml" - changed_when: "'Installing ' in galaxy_collection_result.stdout" - environment: "{{ additional_collections_env | combine(galaxy_task_env) }}" - when: - - "ansible_version.full is version_compare('2.9', '>=')" - - collections_enabled|bool - tags: - - install_collections + # We combine our additional_galaxy_env into galaxy_task_env so that our values are preferred over anything a user would set + environment: "{{ galaxy_task_env | combine(additional_galaxy_env) }}" + vars: + verbosity: "{{ (ansible_verbosity) | ternary('-'+'v'*ansible_verbosity, '') }}" diff --git a/awx/resource_api.py b/awx/resource_api.py new file mode 100644 index 000000000000..10c2eac4ed51 --- /dev/null +++ b/awx/resource_api.py @@ -0,0 +1,42 @@ +from ansible_base.resource_registry.registry import ParentResource, ResourceConfig, ServiceAPIConfig, SharedResource +from ansible_base.rbac.models import RoleDefinition + +from ansible_base.resource_registry.shared_types import ( + FeatureFlagType, + RoleDefinitionType, + OrganizationType, + TeamType, + UserType, +) +from ansible_base.feature_flags.models import AAPFlag +from awx.main import models + + +class APIConfig(ServiceAPIConfig): + service_type = "awx" + + +RESOURCE_LIST = ( + ResourceConfig( + models.Organization, + shared_resource=SharedResource(serializer=OrganizationType, is_provider=False), + ), + ResourceConfig( + models.User, + shared_resource=SharedResource(serializer=UserType, is_provider=False), + name_field="username", + ), + ResourceConfig( + models.Team, + shared_resource=SharedResource(serializer=TeamType, is_provider=False), + parent_resources=[ParentResource(model=models.Organization, field_name="organization")], + ), + ResourceConfig( + RoleDefinition, + shared_resource=SharedResource(serializer=RoleDefinitionType, is_provider=False), + ), + ResourceConfig( + AAPFlag, + shared_resource=SharedResource(serializer=FeatureFlagType, is_provider=False), + ), +) diff --git a/awx/settings/__init__.py b/awx/settings/__init__.py index e484e62be15d..78afed70bdfc 100644 --- a/awx/settings/__init__.py +++ b/awx/settings/__init__.py @@ -1,2 +1,83 @@ # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. +import os +import copy +from ansible_base.lib.dynamic_config import ( + factory, + export, + load_envvars, + load_python_file_with_injected_context, + load_standard_settings_files, +) +from .functions import ( + assert_production_settings, + merge_application_name, + add_backwards_compatibility, + load_extra_development_files, +) + +add_backwards_compatibility() + +# Create a the standard DYNACONF instance which will come with DAB defaults +# This loads defaults.py and environment specific file e.g: development_defaults.py +DYNACONF = factory( + __name__, + "AWX", + environments=("development", "production", "quiet", "kube"), + settings_files=["defaults.py"], +) + +# Store snapshot before loading any custom config file +DYNACONF.set( + "DEFAULTS_SNAPSHOT", + copy.deepcopy(DYNACONF.as_dict(internal=False)), + loader_identifier="awx.settings:DEFAULTS_SNAPSHOT", +) + +############################################################################################# +# Settings loaded before this point will be allowed to be overridden by the database settings +# Any settings loaded after this point will be marked as as a read_only database setting +############################################################################################# + +# Load extra settings files from the following directories +# /etc/tower/conf.d/ and /etc/tower/ +# this is the legacy location, kept for backwards compatibility +settings_dir = os.environ.get('AWX_SETTINGS_DIR', '/etc/tower/conf.d/') +settings_files_path = os.path.join(settings_dir, '*.py') +settings_file_path = os.environ.get('AWX_SETTINGS_FILE', '/etc/tower/settings.py') +load_python_file_with_injected_context(settings_files_path, settings=DYNACONF) +load_python_file_with_injected_context(settings_file_path, settings=DYNACONF) + +# Load extra settings files from the following directories +# /etc/ansible-automation-platform/{settings,flags,.secrets}.yaml +# and /etc/ansible-automation-platform/awx/{settings,flags,.secrets}.yaml +# this is the new standard location for all services +load_standard_settings_files(DYNACONF) + +# Load optional development only settings files +load_extra_development_files(DYNACONF) + +# Check at least one setting file has been loaded in production mode +assert_production_settings(DYNACONF, settings_dir, settings_file_path) + +# Load envvars at the end to allow them to override everything loaded so far +load_envvars(DYNACONF) + +# When deployed as part of AAP (RESOURCE_SERVER__URL is set), enforce JWT-only +# authentication. This ensures all requests go through the gateway and prevents +# direct API access to Controller bypassing the platform's authentication. +if DYNACONF.get('RESOURCE_SERVER__URL', None): + DYNACONF.set( + "REST_FRAMEWORK__DEFAULT_AUTHENTICATION_CLASSES", + ['ansible_base.jwt_consumer.awx.auth.AwxJWTAuthentication'], + ) + +# This must run after all custom settings are loaded +DYNACONF.update( + merge_application_name(DYNACONF), + loader_identifier="awx.settings:merge_application_name", + merge=True, +) + +# Update django.conf.settings with DYNACONF values +export(__name__, DYNACONF) diff --git a/awx/settings/application_name.py b/awx/settings/application_name.py new file mode 100644 index 000000000000..ac7e40553e00 --- /dev/null +++ b/awx/settings/application_name.py @@ -0,0 +1,36 @@ +import os +import sys + + +def get_service_name(argv): + ''' + Return best-effort guess as to the name of this service + ''' + for arg in argv: + if arg == '-m': + continue + if 'python' in arg: + continue + if 'manage' in arg: + continue + if arg.startswith('run_'): + return arg[len('run_') :] + return arg + + +def get_application_name(CLUSTER_HOST_ID, function=''): + if function: + function = f'_{function}' + return f'awx-{os.getpid()}-{get_service_name(sys.argv)}{function}-{CLUSTER_HOST_ID}'[:63] + + +def set_application_name(DATABASES, CLUSTER_HOST_ID, function=''): + """In place modification of DATABASES to set the application name for the connection.""" + # If settings files were not properly passed DATABASES could be {} at which point we don't need to set the app name. + if not DATABASES or 'default' not in DATABASES: + return + + if 'sqlite3' in DATABASES['default']['ENGINE']: + return + options_dict = DATABASES['default'].setdefault('OPTIONS', dict()) + options_dict['application_name'] = get_application_name(CLUSTER_HOST_ID, function) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 4d18540bcda6..30874b6d61eb 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -1,23 +1,12 @@ # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. +# Python import base64 import os import re # noqa -import sys import tempfile import socket -from datetime import timedelta - - -if "pytest" in sys.modules: - from unittest import mock - - with mock.patch('__main__.__builtins__.dir', return_value=[]): - import ldap -else: - import ldap - DEBUG = True SQL_DEBUG = DEBUG @@ -42,6 +31,18 @@ } } +# Special database overrides for dispatcher connections listening to pg_notify +LISTENER_DATABASES = { + 'default': { + 'OPTIONS': { + 'keepalives': 1, + 'keepalives_idle': 5, + 'keepalives_interval': 5, + 'keepalives_count': 5, + }, + } +} + # Whether or not the deployment is a K8S-based deployment # In K8S-based deployments, instances have zero capacity - all playbook # automation is intended to flow through defined Container Groups that @@ -51,6 +52,7 @@ AWX_CONTAINER_GROUP_K8S_API_TIMEOUT = 10 AWX_CONTAINER_GROUP_DEFAULT_NAMESPACE = os.getenv('MY_POD_NAMESPACE', 'default') +AWX_CONTAINER_GROUP_DEFAULT_JOB_LABEL = os.getenv('AWX_CONTAINER_GROUP_DEFAULT_JOB_LABEL', 'ansible_job') # Timeout when waiting for pod to enter running state. If the pod is still in pending state , it will be terminated. Valid time units are "s", "m", "h". Example : "5m" , "10s". AWX_CONTAINER_GROUP_POD_PENDING_TIMEOUT = "2h" @@ -77,13 +79,12 @@ # to load the internationalization machinery. USE_I18N = True -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale -USE_L10N = True - USE_TZ = True -STATICFILES_DIRS = [os.path.join(BASE_DIR, 'ui', 'build', 'static'), os.path.join(BASE_DIR, 'static')] +STATICFILES_DIRS = [ + os.path.join(BASE_DIR, 'ui', 'build'), + os.path.join(BASE_DIR, 'static'), +] # Absolute filesystem path to the directory where static file are collected via # the collectstatic command. @@ -103,6 +104,7 @@ MEDIA_URL = '/media/' LOGIN_URL = '/api/login/' +LOGOUT_ALLOWED_HOSTS = None # Absolute filesystem path to the directory to host projects (with playbooks). # This directory should not be web-accessible. @@ -116,9 +118,6 @@ # Absolute filesystem path to the directory to store logs LOG_ROOT = '/var/log/tower/' -# The heartbeat file for the scheduler -SCHEDULE_METADATA_LOCATION = os.path.join(BASE_DIR, '.tower_cycle') - # Django gettext files path: locale//LC_MESSAGES/django.po, django.mo LOCALE_PATHS = (os.path.join(BASE_DIR, 'locale'),) @@ -129,6 +128,16 @@ # Note: This setting may be overridden by database settings. SCHEDULE_MAX_JOBS = 10 +# Bulk API related settings +# Maximum number of jobs that can be launched in 1 bulk job +BULK_JOB_MAX_LAUNCH = 100 + +# Maximum number of host that can be created in 1 bulk host create +BULK_HOST_MAX_CREATE = 100 + +# Maximum number of host that can be deleted in 1 bulk host delete +BULK_HOST_MAX_DELETE = 250 + SITE_ID = 1 # Make this unique, and don't share it with anybody. @@ -156,6 +165,11 @@ # REMOTE_HOST_HEADERS will be trusted unconditionally') PROXY_IP_ALLOWED_LIST = [] +# If we are behind a reverse proxy/load balancer, use this setting to +# allow the scheme://addresses from which Tower should trust csrf requests from +# If this setting is an empty list (the default), we will only trust ourself +CSRF_TRUSTED_ORIGINS = [] + CUSTOM_VENV_PATHS = [] # Warning: this is a placeholder for a database setting @@ -203,7 +217,7 @@ # The number of seconds to buffer callback receiver bulk # writes in memory before flushing via JobEvent.objects.bulk_create() -JOB_EVENT_BUFFER_SECONDS = 0.1 +JOB_EVENT_BUFFER_SECONDS = 1 # The interval at which callback receiver statistics should be # recorded @@ -215,6 +229,9 @@ # The number of job events to migrate per-transaction when moving from int -> bigint JOB_EVENT_MIGRATION_CHUNK_SIZE = 1000000 +# The prefix of the redis key that stores metrics +SUBSYSTEM_METRICS_REDIS_KEY_PREFIX = "awx_metrics" + # Histogram buckets for the callback_receiver_batch_events_insert_db metric SUBSYSTEM_METRICS_BATCH_INSERT_BUCKETS = [10, 50, 150, 350, 650, 2000] @@ -235,6 +252,7 @@ # We have the grace period so the task manager can bail out before the timeout. TASK_MANAGER_TIMEOUT = 300 TASK_MANAGER_TIMEOUT_GRACE_PERIOD = 60 +TASK_MANAGER_LOCK_TIMEOUT = TASK_MANAGER_TIMEOUT + TASK_MANAGER_TIMEOUT_GRACE_PERIOD # Number of seconds _in addition to_ the task manager timeout a job can stay # in waiting without being reaped @@ -251,6 +269,9 @@ # Note: This setting may be overridden by database settings. SESSION_COOKIE_AGE = 1800 +# Option to change userLoggedIn cookie SameSite policy. +USER_COOKIE_SAMESITE = 'Lax' + # Name of the cookie that contains the session information. # Note: Changing this value may require changes to any clients. SESSION_COOKIE_NAME = 'awx_sessionid' @@ -283,14 +304,21 @@ 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.request', 'awx.ui.context_processors.csp', 'awx.ui.context_processors.version', - 'social_django.context_processors.backends', - 'social_django.context_processors.login_redirect', ], 'builtins': ['awx.main.templatetags.swagger'], + 'libraries': { + "ansible_base.lib.templatetags.requests": "ansible_base.lib.templatetags.requests", + "ansible_base.lib.templatetags.util": "ansible_base.lib.templatetags.util", + }, }, - 'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'ui', 'build'), os.path.join(BASE_DIR, 'ui', 'public')], + 'DIRS': [ + os.path.join(BASE_DIR, 'templates'), + os.path.join(BASE_DIR, 'ui', 'public'), + os.path.join(BASE_DIR, 'ui', 'build', 'awx'), + ], }, ] @@ -308,22 +336,26 @@ # According to channels 4.0 docs you install daphne instead of channels now 'daphne', 'django.contrib.staticfiles', - 'oauth2_provider', 'rest_framework', 'django_extensions', 'polymorphic', - 'taggit', - 'social_django', 'django_guid', 'corsheaders', 'awx.conf', 'awx.main', 'awx.api', 'awx.ui', - 'awx.sso', 'solo', + 'ansible_base.rest_filters', + 'ansible_base.jwt_consumer', + 'ansible_base.resource_registry', + 'ansible_base.rbac', + 'ansible_base.feature_flags', + 'ansible_base.api_documentation', + 'flags', ] + INTERNAL_IPS = ('127.0.0.1',) MAX_PAGE_SIZE = 200 @@ -331,17 +363,11 @@ 'DEFAULT_PAGINATION_CLASS': 'awx.api.pagination.Pagination', 'PAGE_SIZE': 25, 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'awx.api.authentication.LoggedOAuth2Authentication', + 'ansible_base.jwt_consumer.awx.auth.AwxJWTAuthentication', 'awx.api.authentication.SessionAuthentication', 'awx.api.authentication.LoggedBasicAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ('awx.api.permissions.ModelAccessPermission',), - 'DEFAULT_FILTER_BACKENDS': ( - 'awx.api.filters.TypeFilterBackend', - 'awx.api.filters.FieldLookupBackend', - 'rest_framework.filters.SearchFilter', - 'awx.api.filters.OrderByBackend', - ), 'DEFAULT_PARSER_CLASSES': ('awx.api.parsers.JSONParser',), 'DEFAULT_RENDERER_CLASSES': ('awx.api.renderers.DefaultJSONRenderer', 'awx.api.renderers.BrowsableAPIRenderer'), 'DEFAULT_METADATA_CLASS': 'awx.api.metadata.Metadata', @@ -349,65 +375,15 @@ 'VIEW_DESCRIPTION_FUNCTION': 'awx.api.generics.get_view_description', 'NON_FIELD_ERRORS_KEY': '__all__', 'DEFAULT_VERSION': 'v2', - # For swagger schema generation + # For OpenAPI schema generation with drf-spectacular # see https://github.com/encode/django-rest-framework/pull/6532 - 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', # 'URL_FORMAT_OVERRIDE': None, } -AUTHENTICATION_BACKENDS = ( - 'awx.sso.backends.LDAPBackend', - 'awx.sso.backends.LDAPBackend1', - 'awx.sso.backends.LDAPBackend2', - 'awx.sso.backends.LDAPBackend3', - 'awx.sso.backends.LDAPBackend4', - 'awx.sso.backends.LDAPBackend5', - 'awx.sso.backends.RADIUSBackend', - 'awx.sso.backends.TACACSPlusBackend', - 'social_core.backends.google.GoogleOAuth2', - 'social_core.backends.github.GithubOAuth2', - 'social_core.backends.github.GithubOrganizationOAuth2', - 'social_core.backends.github.GithubTeamOAuth2', - 'social_core.backends.github_enterprise.GithubEnterpriseOAuth2', - 'social_core.backends.github_enterprise.GithubEnterpriseOrganizationOAuth2', - 'social_core.backends.github_enterprise.GithubEnterpriseTeamOAuth2', - 'social_core.backends.open_id_connect.OpenIdConnectAuth', - 'social_core.backends.azuread.AzureADOAuth2', - 'awx.sso.backends.SAMLAuth', - 'awx.main.backends.AWXModelBackend', -) - - -# Django OAuth Toolkit settings -OAUTH2_PROVIDER_APPLICATION_MODEL = 'main.OAuth2Application' -OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = 'main.OAuth2AccessToken' -OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = 'oauth2_provider.RefreshToken' - -OAUTH2_PROVIDER = {'ACCESS_TOKEN_EXPIRE_SECONDS': 31536000000, 'AUTHORIZATION_CODE_EXPIRE_SECONDS': 600, 'REFRESH_TOKEN_EXPIRE_SECONDS': 2628000} -ALLOW_OAUTH2_FOR_EXTERNAL_USERS = False - -# LDAP server (default to None to skip using LDAP authentication). -# Note: This setting may be overridden by database settings. -AUTH_LDAP_SERVER_URI = None - -# Disable LDAP referrals by default (to prevent certain LDAP queries from -# hanging with AD). -# Note: This setting may be overridden by database settings. -AUTH_LDAP_CONNECTION_OPTIONS = {ldap.OPT_REFERRALS: 0, ldap.OPT_NETWORK_TIMEOUT: 30} +# SWAGGER_SETTINGS removed - migrated to drf-spectacular (see SPECTACULAR_SETTINGS below) -# Radius server settings (default to empty string to skip using Radius auth). -# Note: These settings may be overridden by database settings. -RADIUS_SERVER = '' -RADIUS_PORT = 1812 -RADIUS_SECRET = '' - -# TACACS+ settings (default host to empty string to skip using TACACS+ auth). -# Note: These settings may be overridden by database settings. -TACACSPLUS_HOST = '' -TACACSPLUS_PORT = 49 -TACACSPLUS_SECRET = '' -TACACSPLUS_SESSION_TIMEOUT = 5 -TACACSPLUS_AUTH_PROTOCOL = 'ascii' +AUTHENTICATION_BACKENDS = ('awx.main.backends.AWXModelBackend',) # Enable / Disable HTTP Basic Authentication used in the API browser # Note: Session limits are not enforced when using HTTP Basic Authentication. @@ -437,113 +413,46 @@ EXECUTION_NODE_REMEDIATION_CHECKS = 60 * 30 # once every 30 minutes check if an execution node errors have been resolved # Amount of time dispatcher will try to reconnect to database for jobs and consuming new work -DISPATCHER_DB_DOWNTOWN_TOLLERANCE = 40 +DISPATCHER_DB_DOWNTIME_TOLERANCE = 40 BROKER_URL = 'unix:///var/run/redis/redis.sock' -CELERYBEAT_SCHEDULE = { - 'tower_scheduler': {'task': 'awx.main.tasks.system.awx_periodic_scheduler', 'schedule': timedelta(seconds=30), 'options': {'expires': 20}}, - 'cluster_heartbeat': { +REDIS_RETRY_COUNT = 3 # Number of retries for Redis connection errors +REDIS_BACKOFF_CAP = 1.0 # Maximum backoff delay in seconds for Redis retries +REDIS_BACKOFF_BASE = 0.5 # Base for exponential backoff calculation for Redis retries + +DISPATCHER_SCHEDULE = { + 'awx.main.tasks.system.awx_periodic_scheduler': {'task': 'awx.main.tasks.system.awx_periodic_scheduler', 'schedule': 30, 'options': {'expires': 20}}, + 'awx.main.tasks.system.cluster_node_heartbeat': { 'task': 'awx.main.tasks.system.cluster_node_heartbeat', - 'schedule': timedelta(seconds=CLUSTER_NODE_HEARTBEAT_PERIOD), + 'schedule': CLUSTER_NODE_HEARTBEAT_PERIOD, 'options': {'expires': 50}, }, - 'gather_analytics': {'task': 'awx.main.tasks.system.gather_analytics', 'schedule': timedelta(minutes=5)}, - 'task_manager': {'task': 'awx.main.scheduler.tasks.task_manager', 'schedule': timedelta(seconds=20), 'options': {'expires': 20}}, - 'dependency_manager': {'task': 'awx.main.scheduler.tasks.dependency_manager', 'schedule': timedelta(seconds=20), 'options': {'expires': 20}}, - 'k8s_reaper': {'task': 'awx.main.tasks.system.awx_k8s_reaper', 'schedule': timedelta(seconds=60), 'options': {'expires': 50}}, - 'receptor_reaper': {'task': 'awx.main.tasks.system.awx_receptor_workunit_reaper', 'schedule': timedelta(seconds=60)}, - 'send_subsystem_metrics': {'task': 'awx.main.analytics.analytics_tasks.send_subsystem_metrics', 'schedule': timedelta(seconds=20)}, - 'cleanup_images': {'task': 'awx.main.tasks.system.cleanup_images_and_files', 'schedule': timedelta(hours=3)}, + 'awx.main.tasks.system.gather_analytics': {'task': 'awx.main.tasks.system.gather_analytics', 'schedule': 300}, + 'awx.main.scheduler.tasks.task_manager': {'task': 'awx.main.scheduler.tasks.task_manager', 'schedule': 20, 'options': {'expires': 20}}, + 'awx.main.scheduler.tasks.dependency_manager': {'task': 'awx.main.scheduler.tasks.dependency_manager', 'schedule': 20, 'options': {'expires': 20}}, + 'awx.main.tasks.system.awx_k8s_reaper': {'task': 'awx.main.tasks.system.awx_k8s_reaper', 'schedule': 60, 'options': {'expires': 50}}, + 'awx.main.tasks.system.awx_receptor_workunit_reaper': {'task': 'awx.main.tasks.system.awx_receptor_workunit_reaper', 'schedule': 60}, + 'awx.main.analytics.analytics_tasks.send_subsystem_metrics': {'task': 'awx.main.analytics.analytics_tasks.send_subsystem_metrics', 'schedule': 20}, + 'awx.main.tasks.system.cleanup_images_and_files': {'task': 'awx.main.tasks.system.cleanup_images_and_files', 'schedule': 10800}, + 'awx.main.tasks.host_metrics.cleanup_host_metrics': {'task': 'awx.main.tasks.host_metrics.cleanup_host_metrics', 'schedule': 12600}, + 'awx.main.tasks.host_metrics.host_metric_summary_monthly': {'task': 'awx.main.tasks.host_metrics.host_metric_summary_monthly', 'schedule': 14400}, + 'awx.main.tasks.system.periodic_resource_sync': {'task': 'awx.main.tasks.system.periodic_resource_sync', 'schedule': 900}, + 'awx.main.tasks.host_indirect.cleanup_and_save_indirect_host_entries_fallback': { + 'task': 'awx.main.tasks.host_indirect.cleanup_and_save_indirect_host_entries_fallback', + 'schedule': 3600, + }, } # Django Caching Configuration DJANGO_REDIS_IGNORE_EXCEPTIONS = True -CACHES = {'default': {'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'unix:/var/run/redis/redis.sock?db=1'}} - -# Social Auth configuration. -SOCIAL_AUTH_STRATEGY = 'social_django.strategy.DjangoStrategy' -SOCIAL_AUTH_STORAGE = 'social_django.models.DjangoStorage' -SOCIAL_AUTH_USER_MODEL = 'auth.User' - -_SOCIAL_AUTH_PIPELINE_BASE = ( - 'social_core.pipeline.social_auth.social_details', - 'social_core.pipeline.social_auth.social_uid', - 'social_core.pipeline.social_auth.auth_allowed', - 'social_core.pipeline.social_auth.social_user', - 'social_core.pipeline.user.get_username', - 'social_core.pipeline.social_auth.associate_by_email', - 'social_core.pipeline.user.create_user', - 'awx.sso.social_base_pipeline.check_user_found_or_created', - 'social_core.pipeline.social_auth.associate_user', - 'social_core.pipeline.social_auth.load_extra_data', - 'awx.sso.social_base_pipeline.set_is_active_for_new_user', - 'social_core.pipeline.user.user_details', - 'awx.sso.social_base_pipeline.prevent_inactive_login', -) -SOCIAL_AUTH_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ('awx.sso.social_pipeline.update_user_orgs', 'awx.sso.social_pipeline.update_user_teams') -SOCIAL_AUTH_SAML_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ('awx.sso.saml_pipeline.populate_user', 'awx.sso.saml_pipeline.update_user_flags') -SAML_AUTO_CREATE_OBJECTS = True - -SOCIAL_AUTH_LOGIN_URL = '/' -SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/' -SOCIAL_AUTH_LOGIN_ERROR_URL = '/sso/error/' -SOCIAL_AUTH_INACTIVE_USER_URL = '/sso/inactive/' - -SOCIAL_AUTH_RAISE_EXCEPTIONS = False -SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = False -# SOCIAL_AUTH_SLUGIFY_USERNAMES = True -SOCIAL_AUTH_CLEAN_USERNAMES = True - -SOCIAL_AUTH_SANITIZE_REDIRECTS = True -SOCIAL_AUTH_REDIRECT_IS_HTTPS = False +CACHES = {'default': {'BACKEND': 'awx.main.cache.AWXRedisCache', 'LOCATION': 'unix:///var/run/redis/redis.sock?db=1'}} -# Note: These settings may be overridden by database settings. -SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '' -SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '' -SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['profile'] - -SOCIAL_AUTH_GITHUB_KEY = '' -SOCIAL_AUTH_GITHUB_SECRET = '' -SOCIAL_AUTH_GITHUB_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_ORG_KEY = '' -SOCIAL_AUTH_GITHUB_ORG_SECRET = '' -SOCIAL_AUTH_GITHUB_ORG_NAME = '' -SOCIAL_AUTH_GITHUB_ORG_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_TEAM_KEY = '' -SOCIAL_AUTH_GITHUB_TEAM_SECRET = '' -SOCIAL_AUTH_GITHUB_TEAM_ID = '' -SOCIAL_AUTH_GITHUB_TEAM_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID = '' -SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SCOPE = ['user:email', 'read:org'] - -SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = '' -SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = '' - -SOCIAL_AUTH_SAML_SP_ENTITY_ID = '' -SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = '' -SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = '' -SOCIAL_AUTH_SAML_ORG_INFO = {} -SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {} -SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {} -SOCIAL_AUTH_SAML_ENABLED_IDPS = {} - -SOCIAL_AUTH_SAML_ORGANIZATION_ATTR = {} -SOCIAL_AUTH_SAML_TEAM_ATTR = {} -SOCIAL_AUTH_SAML_USER_FLAGS_BY_ATTR = {} +ROLE_SINGLETON_USER_RELATIONSHIP = '' +ROLE_SINGLETON_TEAM_RELATIONSHIP = '' + +# We want to short-circuit RBAC methods to get permission to system admins and auditors +ROLE_BYPASS_SUPERUSER_FLAGS = ['is_superuser'] +ROLE_BYPASS_ACTION_FLAGS = {'view': 'is_system_auditor'} # Any ANSIBLE_* settings will be passed to the task runner subprocess # environment @@ -612,6 +521,10 @@ # Automatically remove nodes that have missed their heartbeats after some time AWX_AUTO_DEPROVISION_INSTANCES = False + +# If True, allow users to be assigned to roles that were created via JWT +ALLOW_LOCAL_ASSIGNING_JWT_ROLES = True + # Enable Pendo on the UI, possible values are 'off', 'anonymous', and 'detailed' # Note: This setting may be overridden by database settings. PENDO_TRACKING_STATE = "off" @@ -669,6 +582,12 @@ VMWARE_VALIDATE_CERTS = False +# ----------------- +# -- VMware ESXi -- +# ----------------- +# TODO: Verify matches with AAP-53978 solution in awx-plugins +VMWARE_ESXI_EXCLUDE_EMPTY_GROUPS = True + # --------------------------- # -- Google Compute Engine -- # --------------------------- @@ -712,10 +631,10 @@ # --------------------- # ----- Foreman ----- # --------------------- -SATELLITE6_ENABLED_VAR = 'foreman_enabled' +SATELLITE6_ENABLED_VAR = 'foreman_enabled,foreman.enabled' SATELLITE6_ENABLED_VALUE = 'True' SATELLITE6_EXCLUDE_EMPTY_GROUPS = True -SATELLITE6_INSTANCE_ID_VAR = 'foreman_id' +SATELLITE6_INSTANCE_ID_VAR = 'foreman_id,foreman.id' # SATELLITE6_GROUP_PREFIX and SATELLITE6_GROUP_PATTERNS defined in source vars # ---------------- @@ -726,6 +645,19 @@ INSIGHTS_INSTANCE_ID_VAR = 'insights_id' INSIGHTS_EXCLUDE_EMPTY_GROUPS = False +# ---------------- +# -- Terraform State -- +# ---------------- +# TERRAFORM_ENABLED_VAR = +# TERRAFORM_ENABLED_VALUE = +TERRAFORM_INSTANCE_ID_VAR = 'id' +TERRAFORM_EXCLUDE_EMPTY_GROUPS = True + +# ------------------------ +# OpenShift Virtualization +# ------------------------ +OPENSHIFT_VIRTUALIZATION_EXCLUDE_EMPTY_GROUPS = True + # --------------------- # ----- Custom ----- # --------------------- @@ -742,6 +674,13 @@ SCM_EXCLUDE_EMPTY_GROUPS = False # SCM_INSTANCE_ID_VAR = +# ---------------- +# -- Constructed -- +# ---------------- +CONSTRUCTED_INSTANCE_ID_VAR = 'remote_tower_id' + +CONSTRUCTED_EXCLUDE_EMPTY_GROUPS = False + # --------------------- # -- Activity Stream -- # --------------------- @@ -758,31 +697,26 @@ DISABLE_LOCAL_AUTH = False # Note: This setting may be overridden by database settings. -TOWER_URL_BASE = "https://towerhost" +TOWER_URL_BASE = "https://platformhost" INSIGHTS_URL_BASE = "https://example.org" INSIGHTS_AGENT_MIME = 'application/example' # See https://github.com/ansible/awx-facts-playbooks INSIGHTS_SYSTEM_ID_FILE = '/etc/redhat-access-insights/machine-id' - -TOWER_SETTINGS_MANIFEST = {} +INSIGHTS_CERT_PATH = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" # Settings related to external logger configuration LOG_AGGREGATOR_ENABLED = False LOG_AGGREGATOR_TCP_TIMEOUT = 5 LOG_AGGREGATOR_VERIFY_CERT = True LOG_AGGREGATOR_LEVEL = 'INFO' -LOG_AGGREGATOR_MAX_DISK_USAGE_GB = 1 +LOG_AGGREGATOR_ACTION_QUEUE_SIZE = 131072 +LOG_AGGREGATOR_ACTION_MAX_DISK_USAGE_GB = 1 # Action queue LOG_AGGREGATOR_MAX_DISK_USAGE_PATH = '/var/lib/awx' LOG_AGGREGATOR_RSYSLOGD_DEBUG = False LOG_AGGREGATOR_RSYSLOGD_ERROR_LOG_FILE = '/var/log/tower/rsyslog.err' API_400_ERROR_LOG_FORMAT = 'status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}' -# The number of retry attempts for websocket session establishment -# If you're encountering issues establishing websockets in a cluster, -# raising this value can help -CHANNEL_LAYER_RECEIVE_MAX_RETRY = 10 - ASGI_APPLICATION = "awx.main.routing.application" CHANNEL_LAYERS = { @@ -806,7 +740,6 @@ 'json': {'()': 'awx.main.utils.formatters.LogstashFormatter'}, 'timed_import': {'()': 'awx.main.utils.formatters.TimeFormatter', 'format': '%(relativeSeconds)9.3f %(levelname)-8s %(message)s'}, 'dispatcher': {'format': '%(asctime)s %(levelname)-8s [%(guid)s] %(name)s PID:%(process)d %(message)s'}, - 'job_lifecycle': {'()': 'awx.main.utils.formatters.JobLifeCycleFormatter'}, }, # Extended below based on install scenario. You probably don't want to add something directly here. # See 'handler_config' below. @@ -827,10 +760,12 @@ 'address': '/var/run/awx-rsyslog/rsyslog.sock', 'filters': ['external_log_enabled', 'dynamic_level_filter', 'guid'], }, + 'otel': {'class': 'logging.NullHandler'}, }, 'loggers': { 'django': {'handlers': ['console']}, 'django.request': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'WARNING'}, + 'ansible_base': {'handlers': ['console', 'file', 'tower_warnings']}, 'daphne': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'INFO'}, 'rest_framework.request': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'WARNING', 'propagate': False}, 'py.warnings': {'handlers': ['console']}, @@ -838,25 +773,28 @@ 'awx.conf': {'handlers': ['null'], 'level': 'WARNING'}, 'awx.conf.settings': {'handlers': ['null'], 'level': 'WARNING'}, 'awx.main': {'handlers': ['null']}, - 'awx.main.commands.run_callback_receiver': {'handlers': ['callback_receiver']}, # level handled by dynamic_level_filter - 'awx.main.dispatch': {'handlers': ['dispatcher']}, + 'awx.main.commands.run_callback_receiver': {'handlers': ['callback_receiver'], 'level': 'INFO'}, # very noisey debug-level logs + 'awx.main.dispatch': {'handlers': ['task_system']}, 'awx.main.consumers': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'INFO'}, - 'awx.main.wsbroadcast': {'handlers': ['wsbroadcast']}, + 'awx.main.rsyslog_configurer': {'handlers': ['rsyslog_configurer']}, + 'awx.main.cache_clear': {'handlers': ['cache_clear']}, + 'awx.main.ws_heartbeat': {'handlers': ['ws_heartbeat']}, + 'awx.main.wsrelay': {'handlers': ['wsrelay']}, 'awx.main.commands.inventory_import': {'handlers': ['inventory_import'], 'propagate': False}, - 'awx.main.tasks': {'handlers': ['task_system', 'external_logger'], 'propagate': False}, - 'awx.main.analytics': {'handlers': ['task_system', 'external_logger'], 'level': 'INFO', 'propagate': False}, - 'awx.main.scheduler': {'handlers': ['task_system', 'external_logger'], 'propagate': False}, + 'awx.main.tasks': {'handlers': ['task_system', 'external_logger', 'console'], 'propagate': False}, + 'awx.main.analytics': {'handlers': ['task_system', 'external_logger', 'console'], 'level': 'INFO', 'propagate': False}, + 'awx.main.scheduler': {'handlers': ['task_system', 'external_logger', 'console'], 'propagate': False}, 'awx.main.access': {'level': 'INFO'}, # very verbose debug-level logs 'awx.main.signals': {'level': 'INFO'}, # very verbose debug-level logs 'awx.api.permissions': {'level': 'INFO'}, # very verbose debug-level logs 'awx.analytics': {'handlers': ['external_logger'], 'level': 'INFO', 'propagate': False}, - 'awx.analytics.broadcast_websocket': {'handlers': ['console', 'file', 'wsbroadcast', 'external_logger'], 'level': 'INFO', 'propagate': False}, + 'awx.analytics.broadcast_websocket': {'handlers': ['console', 'file', 'wsrelay', 'external_logger'], 'level': 'INFO', 'propagate': False}, 'awx.analytics.performance': {'handlers': ['console', 'file', 'tower_warnings', 'external_logger'], 'level': 'DEBUG', 'propagate': False}, - 'awx.analytics.job_lifecycle': {'handlers': ['console', 'job_lifecycle'], 'level': 'DEBUG', 'propagate': False}, - 'django_auth_ldap': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'DEBUG'}, + 'awx.analytics.job_lifecycle': {'handlers': ['console', 'job_lifecycle', 'external_logger'], 'level': 'DEBUG', 'propagate': False}, 'social': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'DEBUG'}, 'system_tracking_migrations': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'DEBUG'}, 'rbac_migrations': {'handlers': ['console', 'file', 'tower_warnings'], 'level': 'DEBUG'}, + 'dispatcherd': {'handlers': ['dispatcher', 'console'], 'level': 'INFO'}, }, } @@ -868,10 +806,13 @@ 'tower_warnings': {'filename': 'tower.log'}, 'callback_receiver': {'filename': 'callback_receiver.log'}, 'dispatcher': {'filename': 'dispatcher.log', 'formatter': 'dispatcher'}, - 'wsbroadcast': {'filename': 'wsbroadcast.log'}, + 'wsrelay': {'filename': 'wsrelay.log'}, 'task_system': {'filename': 'task_system.log'}, 'rbac_migrations': {'filename': 'tower_rbac_migrations.log'}, - 'job_lifecycle': {'filename': 'job_lifecycle.log', 'formatter': 'job_lifecycle'}, + 'job_lifecycle': {'filename': 'job_lifecycle.log'}, + 'rsyslog_configurer': {'filename': 'rsyslog_configurer.log'}, + 'cache_clear': {'filename': 'cache_clear.log'}, + 'ws_heartbeat': {'filename': 'ws_heartbeat.log'}, } # If running on a VM, we log to files. When running in a container, we log to stdout. @@ -926,14 +867,25 @@ # Allow ansible-runner to store env folder (may contain sensitive information) AWX_RUNNER_OMIT_ENV_FILES = True -# Allow ansible-runner to save ansible output (may cause performance issues) +# Allow ansible-runner to save ansible output +# (changing to False may cause performance issues) AWX_RUNNER_SUPPRESS_OUTPUT_FILE = True +# https://github.com/ansible/ansible-runner/pull/1191/files +# Interval in seconds between the last message and keep-alive messages that +# ansible-runner will send +AWX_RUNNER_KEEPALIVE_SECONDS = 0 + # Delete completed work units in receptor RECEPTOR_RELEASE_WORK = True +RECEPTOR_KEEP_WORK_ON_ERROR = False + +# K8S only. Use receptor_log_level on AWX spec to set this properly +RECEPTOR_LOG_LEVEL = 'info' MIDDLEWARE = [ 'django_guid.middleware.guid_middleware', + 'ansible_base.lib.middleware.logging.log_request.LogTracebackMiddleware', 'awx.main.middleware.SettingsCacheMiddleware', 'awx.main.middleware.TimingMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -945,7 +897,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'awx.main.middleware.DisableLocalAuthMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - 'awx.sso.middleware.SocialAuthMiddleware', + 'awx.main.middleware.OptionalURLPrefixPath', 'crum.CurrentRequestUserMiddleware', 'awx.main.middleware.URLModificationMiddleware', 'awx.main.middleware.SessionTimeoutMiddleware', @@ -977,6 +929,9 @@ # How often websocket process will generate stats BROADCAST_WEBSOCKET_STATS_POLL_RATE_SECONDS = 5 +# How often should web instances advertise themselves? +BROADCAST_WEBSOCKET_BEACON_FROM_WEB_RATE_SECONDS = 15 + DJANGO_GUID = {'GUID_HEADER_NAME': 'X-API-Request-Id'} # Name of the default task queue @@ -1003,3 +958,185 @@ # This is overridden downstream via /etc/tower/conf.d/cluster_host_id.py CLUSTER_HOST_ID = socket.gethostname() + +# License compliance for total host count. Possible values: +# - '': No model - Subscription not counted from Host Metrics +# - 'unique_managed_hosts': Compliant = automated - deleted hosts (using /api/v2/host_metrics/) +SUBSCRIPTION_USAGE_MODEL = '' + +# Default URL and query params for obtaining valid AAP subscriptions +SUBSCRIPTIONS_RHSM_URL = 'https://console.redhat.com/api/rhsm/v2/products?include=providedProducts&oids=480&status=Active' + +# Host metrics cleanup - last time of the task/command run +CLEANUP_HOST_METRICS_LAST_TS = None +# Host metrics cleanup - minimal interval between two cleanups in days +CLEANUP_HOST_METRICS_INTERVAL = 30 # days +# Host metrics cleanup - soft-delete HostMetric records with last_automation < [threshold] (in months) +CLEANUP_HOST_METRICS_SOFT_THRESHOLD = 12 # months +# Host metrics cleanup +# - delete HostMetric record with deleted=True and last_deleted < [threshold] +# - also threshold for computing HostMetricSummaryMonthly (command/scheduled task) +CLEANUP_HOST_METRICS_HARD_THRESHOLD = 36 # months + +# Host metric summary monthly task - last time of run +HOST_METRIC_SUMMARY_TASK_LAST_TS = None +HOST_METRIC_SUMMARY_TASK_INTERVAL = 7 # days + + +# TODO: cmeyers, replace with with register pattern +# The register pattern is particularly nice for this because we need +# to know the process to start the thread that will be the server. +# The registration location should be the same location as we would +# call MetricsServer.start() +# Note: if we don't get to this TODO, then at least create constants +# for the services strings below. +# TODO: cmeyers, break this out into a separate django app so other +# projects can take advantage. + +METRICS_SERVICE_CALLBACK_RECEIVER = 'callback_receiver' +METRICS_SERVICE_DISPATCHER = 'dispatcherd' +METRICS_SERVICE_WEBSOCKETS = 'websockets' + +METRICS_SUBSYSTEM_CONFIG = { + 'server': { + METRICS_SERVICE_CALLBACK_RECEIVER: { + 'port': 8014, + }, + METRICS_SERVICE_DISPATCHER: { + 'port': 8015, + }, + METRICS_SERVICE_WEBSOCKETS: { + 'port': 8016, + }, + } +} + +# django-ansible-base +ANSIBLE_BASE_TEAM_MODEL = 'main.Team' +ANSIBLE_BASE_ORGANIZATION_MODEL = 'main.Organization' +ANSIBLE_BASE_RESOURCE_CONFIG_MODULE = 'awx.resource_api' + +# Defaults to be overridden by DAB +SPECTACULAR_SETTINGS = { + 'TITLE': 'AWX API', + 'DESCRIPTION': 'AWX API Documentation', + 'VERSION': 'v2', + 'OAS_VERSION': '3.0.3', # Set OpenAPI Specification version to 3.0.3 + 'SERVE_INCLUDE_SCHEMA': False, + 'SCHEMA_PATH_PREFIX': r'/api/v[0-9]', + 'DEFAULT_GENERATOR_CLASS': 'drf_spectacular.generators.SchemaGenerator', + 'SCHEMA_COERCE_PATH_PK_SUFFIX': True, + 'CONTACT': {'email': 'ansible-community@redhat.com'}, + 'LICENSE': {'name': 'Apache License'}, + 'TERMS_OF_SERVICE': 'https://www.google.com/policies/terms/', + # Use our custom schema class that handles swagger_topic and deprecated views + 'DEFAULT_SCHEMA_CLASS': 'awx.api.schema.CustomAutoSchema', + 'COMPONENT_SPLIT_REQUEST': True, + # Postprocessing hook to filter CredentialType enum values + 'POSTPROCESSING_HOOKS': ['awx.api.schema.filter_credential_type_schema'], + 'SWAGGER_UI_SETTINGS': { + 'deepLinking': True, + 'persistAuthorization': True, + 'displayOperationId': True, + }, + # Resolve enum naming collisions with meaningful names + 'ENUM_NAME_OVERRIDES': { + # Status field collisions + 'Status4e1Enum': 'UnifiedJobStatusEnum', + 'Status876Enum': 'JobStatusEnum', + # Job type field collisions + 'JobType8b8Enum': 'JobTemplateJobTypeEnum', + 'JobType95bEnum': 'AdHocCommandJobTypeEnum', + 'JobType963Enum': 'ProjectUpdateJobTypeEnum', + # Verbosity field collisions + 'Verbosity481Enum': 'JobVerbosityEnum', + 'Verbosity8cfEnum': 'InventoryUpdateVerbosityEnum', + # Event field collision + 'Event4d3Enum': 'JobEventEnum', + # Kind field collision + 'Kind362Enum': 'InventoryKindEnum', + }, +} +OAUTH2_PROVIDER = {} + +# Add a postfix to the API URL patterns +# example if set to '' API pattern will be /api +# example if set to 'controller' API pattern will be /api AND /api/controller +OPTIONAL_API_URLPATTERN_PREFIX = '' + +# Add a postfix to the UI URL patterns for UI URL generated by the API +# example if set to '' UI URL generated by the API for jobs would be $TOWER_URL/jobs +# example if set to 'execution' UI URL generated by the API for jobs would be $TOWER_URL/execution/jobs +OPTIONAL_UI_URL_PREFIX = '' + +# Use AWX base view, to give 401 on unauthenticated requests +ANSIBLE_BASE_CUSTOM_VIEW_PARENT = 'awx.api.generics.APIView' + +# If we have a resource server defined, apply local changes to that server +RESOURCE_SERVER_SYNC_ENABLED = True + +# Settings for the ansible_base RBAC system + +# This has been moved to data migration code +ANSIBLE_BASE_ROLE_PRECREATE = {} + +# Name for auto-created roles that give users permissions to what they create +ANSIBLE_BASE_ROLE_CREATOR_NAME = '{cls.__name__} Creator' + +# Use the new Gateway RBAC system for evaluations? You should. We will remove the old system soon. +ANSIBLE_BASE_ROLE_SYSTEM_ACTIVATED = True + +# Permissions a user will get when creating a new item +ANSIBLE_BASE_CREATOR_DEFAULTS = ['change', 'delete', 'execute', 'use', 'adhoc', 'approve', 'update', 'view'] + +# Temporary, for old roles API compatibility, save child permissions at organization level +ANSIBLE_BASE_CACHE_PARENT_PERMISSIONS = True + +# Currently features are enabled to keep compatibility with old system, except custom roles +ANSIBLE_BASE_ALLOW_TEAM_ORG_ADMIN = False +# ANSIBLE_BASE_ALLOW_CUSTOM_ROLES = True +ANSIBLE_BASE_ALLOW_TEAM_PARENTS = False +ANSIBLE_BASE_ALLOW_CUSTOM_TEAM_ROLES = False +ANSIBLE_BASE_ALLOW_SINGLETON_USER_ROLES = True +ANSIBLE_BASE_ALLOW_SINGLETON_TEAM_ROLES = False # System auditor has always been restricted to users +ANSIBLE_BASE_ALLOW_SINGLETON_ROLES_API = False # Do not allow creating user-defined system-wide roles + +# system username for django-ansible-base +SYSTEM_USERNAME = None + +# For indirect host query processing +# if a job is not immediently confirmed to have all events processed +# it will be eligable for processing after this number of minutes +INDIRECT_HOST_QUERY_FALLBACK_MINUTES = 60 + +# If an error happens in event collection, give up after this time +INDIRECT_HOST_QUERY_FALLBACK_GIVEUP_DAYS = 3 + +# Maximum age for indirect host audit records +# Older records will be cleaned up +INDIRECT_HOST_AUDIT_RECORD_MAX_AGE_DAYS = 7 + +# setting for Policy as Code feature +FEATURE_POLICY_AS_CODE_ENABLED = False + +OPA_HOST = '' # The hostname used to connect to the OPA server. If empty, policy enforcement will be disabled. +OPA_PORT = 8181 # The port used to connect to the OPA server. Defaults to 8181. +OPA_SSL = False # Enable or disable the use of SSL to connect to the OPA server. Defaults to false. + +OPA_AUTH_TYPE = 'None' # The authentication type that will be used to connect to the OPA server: "None", "Token", or "Certificate". +OPA_AUTH_TOKEN = '' # The token for authentication to the OPA server. Required when OPA_AUTH_TYPE is "Token". If an authorization header is defined in OPA_AUTH_CUSTOM_HEADERS, it will be overridden by OPA_AUTH_TOKEN. +OPA_AUTH_CLIENT_CERT = '' # The content of the client certificate file for mTLS authentication to the OPA server. Required when OPA_AUTH_TYPE is "Certificate". +OPA_AUTH_CLIENT_KEY = '' # The content of the client key for mTLS authentication to the OPA server. Required when OPA_AUTH_TYPE is "Certificate". +OPA_AUTH_CA_CERT = '' # The content of the CA certificate for mTLS authentication to the OPA server. Required when OPA_AUTH_TYPE is "Certificate". +OPA_AUTH_CUSTOM_HEADERS = {} # Optional custom headers included in requests to the OPA server. Defaults to empty dictionary ({}). +OPA_REQUEST_TIMEOUT = 1.5 # The number of seconds after which the connection to the OPA server will time out. Defaults to 1.5 seconds. +OPA_REQUEST_RETRIES = 2 # The number of retry attempts for connecting to the OPA server. Default is 2. + +# feature flags +FEATURE_INDIRECT_NODE_COUNTING_ENABLED = False +FEATURE_OIDC_WORKLOAD_IDENTITY_ENABLED = False + +# Dispatcher worker lifetime. If set to None, workers will never be retired +# based on age. Note workers will finish their last task before retiring if +# they are busy when they reach retirement age. +WORKER_MAX_LIFETIME_SECONDS = 14400 # seconds diff --git a/awx/settings/development.py b/awx/settings/development.py index 1be4b7295619..5b630c49841a 100644 --- a/awx/settings/development.py +++ b/awx/settings/development.py @@ -1,122 +1,13 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Development settings for AWX project. - -# Python +# This file exists for backwards compatibility only +# the current way of running AWX is to point settings to +# awx/settings/__init__.py as the entry point for the settings +# that is done by exporting: export DJANGO_SETTINGS_MODULE=awx.settings import os -import socket -import copy -import sys -import traceback -import uuid - -# Centos-7 doesn't include the svg mime type -# /usr/lib64/python/mimetypes.py -import mimetypes - -# Django Split Settings -from split_settings.tools import optional, include - -# Load default settings. -from .defaults import * # NOQA - -# awx-manage shell_plus --notebook -NOTEBOOK_ARGUMENTS = ['--NotebookApp.token=', '--ip', '0.0.0.0', '--port', '8888', '--allow-root', '--no-browser'] - -# print SQL queries in shell_plus -SHELL_PLUS_PRINT_SQL = False - -# show colored logs in the dev environment -# to disable this, set `COLOR_LOGS = False` in awx/settings/local_settings.py -LOGGING['handlers']['console']['()'] = 'awx.main.utils.handlers.ColorHandler' # noqa -# task system does not propagate to AWX, so color log these too -LOGGING['handlers']['task_system'] = LOGGING['handlers']['console'].copy() # noqa -COLOR_LOGS = True - -ALLOWED_HOSTS = ['*'] - -mimetypes.add_type("image/svg+xml", ".svg", True) -mimetypes.add_type("image/svg+xml", ".svgz", True) - -# Disallow sending session cookies over insecure connections -SESSION_COOKIE_SECURE = False - -# Disallow sending csrf cookies over insecure connections -CSRF_COOKIE_SECURE = False - -# Disable Pendo on the UI for development/test. -# Note: This setting may be overridden by database settings. -PENDO_TRACKING_STATE = "off" -INSIGHTS_TRACKING_STATE = False - -# debug toolbar and swagger assume that requirements/requirements_dev.txt are installed - -INSTALLED_APPS += ['rest_framework_swagger', 'debug_toolbar'] # NOQA - -MIDDLEWARE = ['debug_toolbar.middleware.DebugToolbarMiddleware'] + MIDDLEWARE # NOQA - -DEBUG_TOOLBAR_CONFIG = {'ENABLE_STACKTRACES': True} - -# Configure a default UUID for development only. -SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' -INSTALL_UUID = '00000000-0000-0000-0000-000000000000' - -# Store a snapshot of default settings at this point before loading any -# customizable config files. -DEFAULTS_SNAPSHOT = {} -this_module = sys.modules[__name__] -for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) - -# If there is an `/etc/tower/settings.py`, include it. -# If there is a `/etc/tower/conf.d/*.py`, include them. -include(optional('/etc/tower/settings.py'), scope=locals()) -include(optional('/etc/tower/conf.d/*.py'), scope=locals()) - -BASE_VENV_PATH = "/var/lib/awx/venv/" -AWX_VENV_PATH = os.path.join(BASE_VENV_PATH, "awx") - -# Use SQLite for unit tests instead of PostgreSQL. If the lines below are -# commented out, Django will create the test_awx-dev database in PostgreSQL to -# run unit tests. -if "pytest" in sys.modules: - CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-{}'.format(str(uuid.uuid4()))}} - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'awx.sqlite3'), # noqa - 'TEST': { - # Test database cannot be :memory: for inventory tests. - 'NAME': os.path.join(BASE_DIR, 'awx_test.sqlite3') # noqa - }, - } - } - -CLUSTER_HOST_ID = socket.gethostname() - -AWX_CALLBACK_PROFILE = True -# ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= -# Disable normal scheduled/triggered task managers (DependencyManager, TaskManager, WorkflowManager). -# Allows user to trigger task managers directly for debugging and profiling purposes. -# Only works in combination with settings.SETTINGS_MODULE == 'awx.settings.development' -AWX_DISABLE_TASK_MANAGERS = False -# ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings") +os.environ.setdefault("AWX_MODE", "development") -if 'sqlite3' not in DATABASES['default']['ENGINE']: # noqa - DATABASES['default'].setdefault('OPTIONS', dict()).setdefault('application_name', f'{CLUSTER_HOST_ID}-{os.getpid()}-{" ".join(sys.argv)}'[:63]) # noqa +from ansible_base.lib.dynamic_config import export +from . import DYNACONF # noqa -# If any local_*.py files are present in awx/settings/, use them to override -# default settings for development. If not present, we can still run using -# only the defaults. -# this needs to stay at the bottom of this file -try: - if os.getenv('AWX_KUBE_DEVEL', False): - include(optional('development_kube.py'), scope=locals()) - else: - include(optional('local_*.py'), scope=locals()) -except ImportError: - traceback.print_exc() - sys.exit(1) +export(__name__, DYNACONF) diff --git a/awx/settings/development_defaults.py b/awx/settings/development_defaults.py new file mode 100644 index 000000000000..49cb1a68b6bc --- /dev/null +++ b/awx/settings/development_defaults.py @@ -0,0 +1,71 @@ +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved. + +# Development settings for AWX project. + +# Python +import os +import socket + +# Centos-7 doesn't include the svg mime type +# /usr/lib64/python/mimetypes.py +import mimetypes + +# awx-manage shell_plus --notebook +NOTEBOOK_ARGUMENTS = ['--NotebookApp.token=', '--ip', '0.0.0.0', '--port', '9888', '--allow-root', '--no-browser'] + +# print SQL queries in shell_plus +SHELL_PLUS_PRINT_SQL = False + +# show colored logs in the dev environment +# to disable this, set `COLOR_LOGS = False` in awx/settings/local_settings.py +COLOR_LOGS = True +LOGGING__handlers__console = '@merge {"()": "awx.main.utils.handlers.ColorHandler"}' + +ALLOWED_HOSTS = ['*'] + +mimetypes.add_type("image/svg+xml", ".svg", True) +mimetypes.add_type("image/svg+xml", ".svgz", True) + +# Disallow sending session cookies over insecure connections +SESSION_COOKIE_SECURE = False + +# Disallow sending csrf cookies over insecure connections +CSRF_COOKIE_SECURE = False + +# Disable Pendo on the UI for development/test. +# Note: This setting may be overridden by database settings. +PENDO_TRACKING_STATE = "off" +INSIGHTS_TRACKING_STATE = False + +# debug toolbar and swagger assume that requirements/requirements_dev.txt are installed +INSTALLED_APPS = "@merge drf_spectacular,debug_toolbar" +MIDDLEWARE = "@insert 0 debug_toolbar.middleware.DebugToolbarMiddleware" + +DEBUG_TOOLBAR_CONFIG = {'ENABLE_STACKTRACES': True} + +# drf-spectacular settings for API schema generation +# SPECTACULAR_SETTINGS moved to defaults.py so it's available in all environments + +# Configure a default UUID for development only. +SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' +INSTALL_UUID = '00000000-0000-0000-0000-000000000000' + +# Ansible base virtualenv paths and enablement +# only used for deprecated fields and management commands for them +BASE_VENV_PATH = os.path.realpath("/var/lib/awx/venv") + +CLUSTER_HOST_ID = socket.gethostname() + +AWX_CALLBACK_PROFILE = True + +# ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= +# Disable normal scheduled/triggered task managers (DependencyManager, TaskManager, WorkflowManager). +# Allows user to trigger task managers directly for debugging and profiling purposes. +# Only works in combination with settings.SETTINGS_MODULE == 'awx.settings.development' +AWX_DISABLE_TASK_MANAGERS = False + +# Needed for launching runserver in debug mode +# ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= + +FEATURE_INDIRECT_NODE_COUNTING_ENABLED = True diff --git a/awx/settings/development_kube.py b/awx/settings/development_kube.py index c30a7fe025fe..e6ba6170c6d7 100644 --- a/awx/settings/development_kube.py +++ b/awx/settings/development_kube.py @@ -1,4 +1,13 @@ -BROADCAST_WEBSOCKET_SECRET = '🤖starscream🤖' -BROADCAST_WEBSOCKET_PORT = 8052 -BROADCAST_WEBSOCKET_VERIFY_CERT = False -BROADCAST_WEBSOCKET_PROTOCOL = 'http' +# This file exists for backwards compatibility only +# the current way of running AWX is to point settings to +# awx/settings/__init__.py as the entry point for the settings +# that is done by exporting: export DJANGO_SETTINGS_MODULE=awx.settings +import os + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings") +os.environ.setdefault("AWX_MODE", "development,kube") + +from ansible_base.lib.dynamic_config import export +from . import DYNACONF # noqa + +export(__name__, DYNACONF) diff --git a/awx/settings/development_quiet.py b/awx/settings/development_quiet.py index c47e78b69d86..5fea2756e908 100644 --- a/awx/settings/development_quiet.py +++ b/awx/settings/development_quiet.py @@ -1,15 +1,13 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. +# This file exists for backwards compatibility only +# the current way of running AWX is to point settings to +# awx/settings/__init__.py as the entry point for the settings +# that is done by exporting: export DJANGO_SETTINGS_MODULE=awx.settings +import os -# Development settings for AWX project, but with DEBUG disabled +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings") +os.environ.setdefault("AWX_MODE", "development,quiet") -# Load development settings. -from defaults import * # NOQA +from ansible_base.lib.dynamic_config import export +from . import DYNACONF # noqa -# Load development settings. -from development import * # NOQA - -# Disable capturing DEBUG -DEBUG = False -TEMPLATE_DEBUG = DEBUG -SQL_DEBUG = DEBUG +export(__name__, DYNACONF) diff --git a/awx/settings/functions.py b/awx/settings/functions.py new file mode 100644 index 000000000000..70be9befdbae --- /dev/null +++ b/awx/settings/functions.py @@ -0,0 +1,86 @@ +import os +from ansible_base.lib.dynamic_config import load_python_file_with_injected_context +from dynaconf import Dynaconf +from .application_name import get_application_name + + +def merge_application_name(settings): + """Return a dynaconf merge dict to set the application name for the connection.""" + data = {} + if "sqlite3" not in settings.get("DATABASES__default__ENGINE", ""): + data["DATABASES__default__OPTIONS__application_name"] = get_application_name(settings.get("CLUSTER_HOST_ID")) + return data + + +def add_backwards_compatibility(): + """Add backwards compatibility for AWX_MODE. + + Before dynaconf integration the usage of AWX settings was supported to be just + DJANGO_SETTINGS_MODULE=awx.settings.production or DJANGO_SETTINGS_MODULE=awx.settings.development + (development_quiet and development_kube were also supported). + + With dynaconf the DJANGO_SETTINGS_MODULE should be set always to "awx.settings" as the only entry point + for settings and then "AWX_MODE" can be set to any of production,development,quiet,kube + or a combination of them separated by comma. + + E.g: + + export DJANGO_SETTINGS_MODULE=awx.settings + export AWX_MODE=production + awx-manage [command] + dynaconf [command] + + If pointing `DJANGO_SETTINGS_MODULE` to `awx.settings.production` or `awx.settings.development` then + this function will set `AWX_MODE` to the correct value. + """ + django_settings_module = os.getenv("DJANGO_SETTINGS_MODULE", "awx.settings") + if django_settings_module == "awx.settings": + return + + current_mode = os.getenv("AWX_MODE", "") + for _module_name in ["development", "production", "development_quiet", "development_kube"]: + if django_settings_module == f"awx.settings.{_module_name}": + _mode = current_mode.split(",") + if "development_" in _module_name and "development" not in current_mode: + _mode.append("development") + _mode_fragment = _module_name.replace("development_", "") + if _mode_fragment not in _mode: + _mode.append(_mode_fragment) + os.environ["AWX_MODE"] = ",".join(_mode) + + +def load_extra_development_files(settings: Dynaconf): + """Load optional development only settings files.""" + if not settings.is_development_mode: + return + + if settings.get_environ("AWX_KUBE_DEVEL"): + load_python_file_with_injected_context("kube_defaults.py", settings=settings) + else: + load_python_file_with_injected_context("local_*.py", settings=settings) + + +def assert_production_settings(settings: Dynaconf, settings_dir: str, settings_file_path: str): # pragma: no cover + """Ensure at least one setting file has been loaded in production mode. + Current systems will require /etc/tower/settings.py and + new systems will require /etc/ansible-automation-platform/*.yaml + """ + if "production" not in settings.current_env.lower(): + return + + required_settings_paths = [ + os.path.dirname(settings_file_path), + "/etc/ansible-automation-platform/", + settings_dir, + ] + + for path in required_settings_paths: + if any([path in os.path.dirname(f) for f in settings._loaded_files]): + break + else: + from django.core.exceptions import ImproperlyConfigured # noqa + + msg = 'No AWX configuration found at %s.' % required_settings_paths + msg += '\nDefine the AWX_SETTINGS_FILE environment variable to ' + msg += 'specify an alternate path.' + raise ImproperlyConfigured(msg) diff --git a/awx/settings/kube_defaults.py b/awx/settings/kube_defaults.py new file mode 100644 index 000000000000..c30a7fe025fe --- /dev/null +++ b/awx/settings/kube_defaults.py @@ -0,0 +1,4 @@ +BROADCAST_WEBSOCKET_SECRET = '🤖starscream🤖' +BROADCAST_WEBSOCKET_PORT = 8052 +BROADCAST_WEBSOCKET_VERIFY_CERT = False +BROADCAST_WEBSOCKET_PROTOCOL = 'http' diff --git a/awx/settings/production.py b/awx/settings/production.py index 3dce95deb08f..bcf483b118cf 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -1,105 +1,13 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Production settings for AWX project. - -# Python +# This file exists for backwards compatibility only +# the current way of running AWX is to point settings to +# awx/settings/__init__.py as the entry point for the settings +# that is done by exporting: export DJANGO_SETTINGS_MODULE=awx.settings import os -import copy -import errno -import sys -import traceback - -# Django Split Settings -from split_settings.tools import optional, include - -# Load default settings. -from .defaults import * # NOQA - -DEBUG = False -TEMPLATE_DEBUG = DEBUG -SQL_DEBUG = DEBUG - -# Clear database settings to force production environment to define them. -DATABASES = {} - -# Clear the secret key to force production environment to define it. -SECRET_KEY = None - -# Hosts/domain names that are valid for this site; required if DEBUG is False -# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts -ALLOWED_HOSTS = [] - -# The heartbeat file for the scheduler -SCHEDULE_METADATA_LOCATION = '/var/lib/awx/.tower_cycle' - -# Ansible base virtualenv paths and enablement -BASE_VENV_PATH = os.path.realpath("/var/lib/awx/venv") - -# Base virtualenv paths and enablement -AWX_VENV_PATH = os.path.join(BASE_VENV_PATH, "awx") - -# Very important that this is editable (not read_only) in the API -AWX_ISOLATION_SHOW_PATHS = [ - '/etc/pki/ca-trust:/etc/pki/ca-trust:O', - '/usr/share/pki:/usr/share/pki:O', -] - -# Store a snapshot of default settings at this point before loading any -# customizable config files. -# -############################################################################################### -# -# Any settings defined after this point will be marked as as a read_only database setting -# -################################################################################################ -DEFAULTS_SNAPSHOT = {} -this_module = sys.modules[__name__] -for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) - -# Load settings from any .py files in the global conf.d directory specified in -# the environment, defaulting to /etc/tower/conf.d/. -settings_dir = os.environ.get('AWX_SETTINGS_DIR', '/etc/tower/conf.d/') -settings_files = os.path.join(settings_dir, '*.py') - -# Load remaining settings from the global settings file specified in the -# environment, defaulting to /etc/tower/settings.py. -settings_file = os.environ.get('AWX_SETTINGS_FILE', '/etc/tower/settings.py') - -# Attempt to load settings from /etc/tower/settings.py first, followed by -# /etc/tower/conf.d/*.py. -try: - include(settings_file, optional(settings_files), scope=locals()) -except ImportError: - traceback.print_exc() - sys.exit(1) -except IOError: - from django.core.exceptions import ImproperlyConfigured - included_file = locals().get('__included_file__', '') - if not included_file or included_file == settings_file: - # The import doesn't always give permission denied, so try to open the - # settings file directly. - try: - e = None - open(settings_file) - except IOError: - pass - if e and e.errno == errno.EACCES: - SECRET_KEY = 'permission-denied' - LOGGING = {} - else: - msg = 'No AWX configuration found at %s.' % settings_file - msg += '\nDefine the AWX_SETTINGS_FILE environment variable to ' - msg += 'specify an alternate path.' - raise ImproperlyConfigured(msg) - else: - raise +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings") +os.environ.setdefault("AWX_MODE", "production") -# The below runs AFTER all of the custom settings are imported. +from ansible_base.lib.dynamic_config import export +from . import DYNACONF # noqa -DATABASES.setdefault('default', dict()).setdefault('OPTIONS', dict()).setdefault( - 'application_name', f'{CLUSTER_HOST_ID}-{os.getpid()}-{" ".join(sys.argv)}'[:63] # NOQA -) # noqa +export(__name__, DYNACONF) diff --git a/awx/settings/production_defaults.py b/awx/settings/production_defaults.py new file mode 100644 index 000000000000..02184abbda8b --- /dev/null +++ b/awx/settings/production_defaults.py @@ -0,0 +1,35 @@ +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved. + +# Production settings for AWX project. + +import os + +DEBUG = False +TEMPLATE_DEBUG = DEBUG +SQL_DEBUG = DEBUG + +# Clear database settings to force production environment to define them. +DATABASES = {} + +# Clear the secret key to force production environment to define it. +SECRET_KEY = None + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [] + +# In production, trust the X-Forwarded-For header set by the reverse proxy +REMOTE_HOST_HEADERS = ['HTTP_X_FORWARDED_FOR'] + +# Ansible base virtualenv paths and enablement +# only used for deprecated fields and management commands for them +BASE_VENV_PATH = os.path.realpath("/var/lib/awx/venv") + +# Very important that this is editable (not read_only) in the API +AWX_ISOLATION_SHOW_PATHS = [ + '/etc/pki/ca-trust:/etc/pki/ca-trust:O', + '/usr/share/pki:/usr/share/pki:O', +] + +del os diff --git a/awx/settings/quiet_defaults.py b/awx/settings/quiet_defaults.py new file mode 100644 index 000000000000..1cb21720f7dd --- /dev/null +++ b/awx/settings/quiet_defaults.py @@ -0,0 +1,8 @@ +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved. +# Development settings for AWX project, but with DEBUG disabled + +# Disable capturing DEBUG +DEBUG = False +TEMPLATE_DEBUG = DEBUG +SQL_DEBUG = DEBUG diff --git a/awx/sso/__init__.py b/awx/sso/__init__.py deleted file mode 100644 index e484e62be15d..000000000000 --- a/awx/sso/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. diff --git a/awx/sso/apps.py b/awx/sso/apps.py deleted file mode 100644 index 6203ca6d6a11..000000000000 --- a/awx/sso/apps.py +++ /dev/null @@ -1,8 +0,0 @@ -# Django -from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ - - -class SSOConfig(AppConfig): - name = 'awx.sso' - verbose_name = _('Single Sign-On') diff --git a/awx/sso/backends.py b/awx/sso/backends.py deleted file mode 100644 index c55f24e7de3d..000000000000 --- a/awx/sso/backends.py +++ /dev/null @@ -1,446 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -from collections import OrderedDict -import logging -import uuid - -import ldap - -# Django -from django.dispatch import receiver -from django.contrib.auth.models import User -from django.conf import settings as django_settings -from django.core.signals import setting_changed -from django.utils.encoding import force_str - -# django-auth-ldap -from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings -from django_auth_ldap.backend import LDAPBackend as BaseLDAPBackend -from django_auth_ldap.backend import populate_user -from django.core.exceptions import ImproperlyConfigured - -# radiusauth -from radiusauth.backends import RADIUSBackend as BaseRADIUSBackend - -# tacacs+ auth -import tacacs_plus - -# social -from social_core.backends.saml import OID_USERID -from social_core.backends.saml import SAMLAuth as BaseSAMLAuth -from social_core.backends.saml import SAMLIdentityProvider as BaseSAMLIdentityProvider - -# Ansible Tower -from awx.sso.models import UserEnterpriseAuth -from awx.sso.common import create_org_and_teams, reconcile_users_org_team_mappings - -logger = logging.getLogger('awx.sso.backends') - - -class LDAPSettings(BaseLDAPSettings): - defaults = dict(list(BaseLDAPSettings.defaults.items()) + list({'ORGANIZATION_MAP': {}, 'TEAM_MAP': {}, 'GROUP_TYPE_PARAMS': {}}.items())) - - def __init__(self, prefix='AUTH_LDAP_', defaults={}): - super(LDAPSettings, self).__init__(prefix, defaults) - - # If a DB-backed setting is specified that wipes out the - # OPT_NETWORK_TIMEOUT, fall back to a sane default - if ldap.OPT_NETWORK_TIMEOUT not in getattr(self, 'CONNECTION_OPTIONS', {}): - options = getattr(self, 'CONNECTION_OPTIONS', {}) - options[ldap.OPT_NETWORK_TIMEOUT] = 30 - self.CONNECTION_OPTIONS = options - - # when specifying `.set_option()` calls for TLS in python-ldap, the - # *order* in which you invoke them *matters*, particularly in Python3, - # where dictionary insertion order is persisted - # - # specifically, it is *critical* that `ldap.OPT_X_TLS_NEWCTX` be set *last* - # this manual sorting puts `OPT_X_TLS_NEWCTX` *after* other TLS-related - # options - # - # see: https://github.com/python-ldap/python-ldap/issues/55 - newctx_option = self.CONNECTION_OPTIONS.pop(ldap.OPT_X_TLS_NEWCTX, None) - self.CONNECTION_OPTIONS = OrderedDict(self.CONNECTION_OPTIONS) - if newctx_option is not None: - self.CONNECTION_OPTIONS[ldap.OPT_X_TLS_NEWCTX] = newctx_option - - -class LDAPBackend(BaseLDAPBackend): - - """ - Custom LDAP backend for AWX. - """ - - settings_prefix = 'AUTH_LDAP_' - - def __init__(self, *args, **kwargs): - self._dispatch_uid = uuid.uuid4() - super(LDAPBackend, self).__init__(*args, **kwargs) - setting_changed.connect(self._on_setting_changed, dispatch_uid=self._dispatch_uid) - - def _on_setting_changed(self, sender, **kwargs): - # If any AUTH_LDAP_* setting changes, force settings to be reloaded for - # this backend instance. - if kwargs.get('setting', '').startswith(self.settings_prefix): - self._settings = None - - def _get_settings(self): - if self._settings is None: - self._settings = LDAPSettings(self.settings_prefix) - return self._settings - - def _set_settings(self, settings): - self._settings = settings - - settings = property(_get_settings, _set_settings) - - def authenticate(self, request, username, password): - if self.settings.START_TLS and ldap.OPT_X_TLS_REQUIRE_CERT in self.settings.CONNECTION_OPTIONS: - # with python-ldap, if you want to set connection-specific TLS - # parameters, you must also specify OPT_X_TLS_NEWCTX = 0 - # see: https://stackoverflow.com/a/29722445 - # see: https://stackoverflow.com/a/38136255 - self.settings.CONNECTION_OPTIONS[ldap.OPT_X_TLS_NEWCTX] = 0 - - if not self.settings.SERVER_URI: - return None - try: - user = User.objects.get(username=username) - if user and (not user.profile or not user.profile.ldap_dn): - return None - except User.DoesNotExist: - pass - - try: - for setting_name, type_ in [('GROUP_SEARCH', 'LDAPSearch'), ('GROUP_TYPE', 'LDAPGroupType')]: - if getattr(self.settings, setting_name) is None: - raise ImproperlyConfigured("{} must be an {} instance.".format(setting_name, type_)) - ldap_user = super(LDAPBackend, self).authenticate(request, username, password) - # If we have an LDAP user and that user we found has an ldap_user internal object and that object has a bound connection - # Then we can try and force an unbind to close the sticky connection - if ldap_user and ldap_user.ldap_user and ldap_user.ldap_user._connection_bound: - logger.debug("Forcing LDAP connection to close") - try: - ldap_user.ldap_user._connection.unbind_s() - ldap_user.ldap_user._connection_bound = False - except Exception: - logger.exception(f"Got unexpected LDAP exception when forcing LDAP disconnect for user {ldap_user}, login will still proceed") - return ldap_user - except Exception: - logger.exception("Encountered an error authenticating to LDAP") - return None - - def get_user(self, user_id): - if not self.settings.SERVER_URI: - return None - return super(LDAPBackend, self).get_user(user_id) - - # Disable any LDAP based authorization / permissions checking. - - def has_perm(self, user, perm, obj=None): - return False - - def has_module_perms(self, user, app_label): - return False - - def get_all_permissions(self, user, obj=None): - return set() - - def get_group_permissions(self, user, obj=None): - return set() - - -class LDAPBackend1(LDAPBackend): - settings_prefix = 'AUTH_LDAP_1_' - - -class LDAPBackend2(LDAPBackend): - settings_prefix = 'AUTH_LDAP_2_' - - -class LDAPBackend3(LDAPBackend): - settings_prefix = 'AUTH_LDAP_3_' - - -class LDAPBackend4(LDAPBackend): - settings_prefix = 'AUTH_LDAP_4_' - - -class LDAPBackend5(LDAPBackend): - settings_prefix = 'AUTH_LDAP_5_' - - -def _decorate_enterprise_user(user, provider): - user.set_unusable_password() - user.save() - enterprise_auth, _ = UserEnterpriseAuth.objects.get_or_create(user=user, provider=provider) - return enterprise_auth - - -def _get_or_set_enterprise_user(username, password, provider): - created = False - try: - user = User.objects.prefetch_related('enterprise_auth').get(username=username) - except User.DoesNotExist: - user = User(username=username) - enterprise_auth = _decorate_enterprise_user(user, provider) - logger.debug("Created enterprise user %s via %s backend." % (username, enterprise_auth.get_provider_display())) - created = True - if created or user.is_in_enterprise_category(provider): - return user - logger.warning("Enterprise user %s already defined in Tower." % username) - - -class RADIUSBackend(BaseRADIUSBackend): - """ - Custom Radius backend to verify license status - """ - - def authenticate(self, request, username, password): - if not django_settings.RADIUS_SERVER: - return None - return super(RADIUSBackend, self).authenticate(request, username, password) - - def get_user(self, user_id): - if not django_settings.RADIUS_SERVER: - return None - user = super(RADIUSBackend, self).get_user(user_id) - if not user.has_usable_password(): - return user - - def get_django_user(self, username, password=None, groups=[], is_staff=False, is_superuser=False): - return _get_or_set_enterprise_user(force_str(username), force_str(password), 'radius') - - -class TACACSPlusBackend(object): - """ - Custom TACACS+ auth backend for AWX - """ - - def authenticate(self, request, username, password): - if not django_settings.TACACSPLUS_HOST: - return None - try: - # Upstream TACACS+ client does not accept non-string, so convert if needed. - auth = tacacs_plus.TACACSClient( - django_settings.TACACSPLUS_HOST, - django_settings.TACACSPLUS_PORT, - django_settings.TACACSPLUS_SECRET, - timeout=django_settings.TACACSPLUS_SESSION_TIMEOUT, - ).authenticate(username, password, authen_type=tacacs_plus.TAC_PLUS_AUTHEN_TYPES[django_settings.TACACSPLUS_AUTH_PROTOCOL]) - except Exception as e: - logger.exception("TACACS+ Authentication Error: %s" % str(e)) - return None - if auth.valid: - return _get_or_set_enterprise_user(username, password, 'tacacs+') - - def get_user(self, user_id): - if not django_settings.TACACSPLUS_HOST: - return None - try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: - return None - - -class TowerSAMLIdentityProvider(BaseSAMLIdentityProvider): - """ - Custom Identity Provider to make attributes to what we expect. - """ - - def get_user_permanent_id(self, attributes): - uid = attributes[self.conf.get('attr_user_permanent_id', OID_USERID)] - if isinstance(uid, str): - return uid - return uid[0] - - def get_attr(self, attributes, conf_key, default_attribute): - """ - Get the attribute 'default_attribute' out of the attributes, - unless self.conf[conf_key] overrides the default by specifying - another attribute to use. - """ - key = self.conf.get(conf_key, default_attribute) - value = attributes[key] if key in attributes else None - # In certain implementations (like https://pagure.io/ipsilon) this value is a string, not a list - if isinstance(value, (list, tuple)): - value = value[0] - if conf_key in ('attr_first_name', 'attr_last_name', 'attr_username', 'attr_email') and value is None: - logger.warning( - "Could not map user detail '%s' from SAML attribute '%s'; " "update SOCIAL_AUTH_SAML_ENABLED_IDPS['%s']['%s'] with the correct SAML attribute.", - conf_key[5:], - key, - self.name, - conf_key, - ) - return str(value) if value is not None else value - - -class SAMLAuth(BaseSAMLAuth): - """ - Custom SAMLAuth backend to verify license status - """ - - def get_idp(self, idp_name): - idp_config = self.setting('ENABLED_IDPS')[idp_name] - return TowerSAMLIdentityProvider(idp_name, **idp_config) - - def authenticate(self, request, *args, **kwargs): - if not all( - [ - django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, - django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT, - django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, - django_settings.SOCIAL_AUTH_SAML_ORG_INFO, - django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, - django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT, - django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS, - ] - ): - return None - user = super(SAMLAuth, self).authenticate(request, *args, **kwargs) - # Comes from https://github.com/omab/python-social-auth/blob/v0.2.21/social/backends/base.py#L91 - if getattr(user, 'is_new', False): - enterprise_auth = _decorate_enterprise_user(user, 'saml') - logger.debug("Created enterprise user %s from %s backend." % (user.username, enterprise_auth.get_provider_display())) - elif user and not user.is_in_enterprise_category('saml'): - return None - if user: - logger.debug("Enterprise user %s already created in Tower." % user.username) - return user - - def get_user(self, user_id): - if not all( - [ - django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, - django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT, - django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, - django_settings.SOCIAL_AUTH_SAML_ORG_INFO, - django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, - django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT, - django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS, - ] - ): - return None - return super(SAMLAuth, self).get_user(user_id) - - -def _update_m2m_from_groups(ldap_user, opts, remove=True): - """ - Hepler function to evaluate the LDAP team/org options to determine if LDAP user should - be a member of the team/org based on their ldap group dns. - - Returns: - True - User should be added - False - User should be removed - None - Users membership should not be changed - """ - if opts is None: - return None - elif not opts: - pass - elif isinstance(opts, bool) and opts is True: - return True - else: - if isinstance(opts, str): - opts = [opts] - # If any of the users groups matches any of the list options - for group_dn in opts: - if not isinstance(group_dn, str): - continue - if ldap_user._get_groups().is_member_of(group_dn): - return True - if remove: - return False - return None - - -@receiver(populate_user, dispatch_uid='populate-ldap-user') -def on_populate_user(sender, **kwargs): - """ - Handle signal from LDAP backend to populate the user object. Update user - organization/team memberships according to their LDAP groups. - """ - user = kwargs['user'] - ldap_user = kwargs['ldap_user'] - backend = ldap_user.backend - - # Boolean to determine if we should force an user update - # to avoid duplicate SQL update statements - force_user_update = False - - # Prefetch user's groups to prevent LDAP queries for each org/team when - # checking membership. - ldap_user._get_groups().get_group_dns() - - # If the LDAP user has a first or last name > $maxlen chars, truncate it - for field in ('first_name', 'last_name'): - max_len = User._meta.get_field(field).max_length - field_len = len(getattr(user, field)) - if field_len > max_len: - setattr(user, field, getattr(user, field)[:max_len]) - force_user_update = True - logger.warning('LDAP user {} has {} > max {} characters'.format(user.username, field, max_len)) - - org_map = getattr(backend.settings, 'ORGANIZATION_MAP', {}) - team_map = getattr(backend.settings, 'TEAM_MAP', {}) - orgs_list = list(org_map.keys()) - team_map = {} - for team_name, team_opts in team_map.items(): - if not team_opts.get('organization', None): - # You can't save the LDAP config in the UI w/o an org (or '' or null as the org) so if we somehow got this condition its an error - logger.error("Team named {} in LDAP team map settings is invalid due to missing organization".format(team_name)) - continue - team_map[team_name] = team_opts['organization'] - - create_org_and_teams(orgs_list, team_map, 'LDAP') - - # Compute in memory what the state is of the different LDAP orgs - org_roles_and_ldap_attributes = {'admin_role': 'admins', 'auditor_role': 'auditors', 'member_role': 'users'} - desired_org_states = {} - for org_name, org_opts in org_map.items(): - remove = bool(org_opts.get('remove', True)) - desired_org_states[org_name] = {} - for org_role_name in org_roles_and_ldap_attributes.keys(): - ldap_name = org_roles_and_ldap_attributes[org_role_name] - opts = org_opts.get(ldap_name, None) - remove = bool(org_opts.get('remove_{}'.format(ldap_name), remove)) - desired_org_states[org_name][org_role_name] = _update_m2m_from_groups(ldap_user, opts, remove) - - # If everything returned None (because there was no configuration) we can remove this org from our map - # This will prevent us from loading the org in the next query - if all(desired_org_states[org_name][org_role_name] is None for org_role_name in org_roles_and_ldap_attributes.keys()): - del desired_org_states[org_name] - - # Compute in memory what the state is of the different LDAP teams - desired_team_states = {} - for team_name, team_opts in team_map.items(): - if 'organization' not in team_opts: - continue - users_opts = team_opts.get('users', None) - remove = bool(team_opts.get('remove', True)) - state = _update_m2m_from_groups(ldap_user, users_opts, remove) - if state is not None: - organization = team_opts['organization'] - if organization not in desired_team_states: - desired_team_states[organization] = {} - desired_team_states[organization][team_name] = {'member_role': state} - - # Check if user.profile is available, otherwise force user.save() - try: - _ = user.profile - except ValueError: - force_user_update = True - finally: - if force_user_update: - user.save() - - # Update user profile to store LDAP DN. - profile = user.profile - if profile.ldap_dn != ldap_user.dn: - profile.ldap_dn = ldap_user.dn - profile.save() - - reconcile_users_org_team_mappings(user, desired_org_states, desired_team_states, 'LDAP') diff --git a/awx/sso/common.py b/awx/sso/common.py deleted file mode 100644 index 4d601bb22e43..000000000000 --- a/awx/sso/common.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) 2022 Ansible, Inc. -# All Rights Reserved. - -import logging - -from django.contrib.contenttypes.models import ContentType -from django.db.utils import IntegrityError -from awx.main.models import Organization, Team - -logger = logging.getLogger('awx.sso.common') - - -def get_orgs_by_ids(): - existing_orgs = {} - for org_id, org_name in Organization.objects.all().values_list('id', 'name'): - existing_orgs[org_name] = org_id - return existing_orgs - - -def reconcile_users_org_team_mappings(user, desired_org_states, desired_team_states, source): - # - # Arguments: - # user - a user object - # desired_org_states: { '': { '': or None } } - # desired_team_states: { '': { '': { '': or None } } } - # source - a text label indicating the "authentication adapter" for debug messages - # - # This function will load the users existing roles and then based on the desired states modify the users roles - # True indicates the user needs to be a member of the role - # False indicates the user should not be a member of the role - # None means this function should not change the users membership of a role - # - - content_types = [] - reconcile_items = [] - if desired_org_states: - content_types.append(ContentType.objects.get_for_model(Organization)) - reconcile_items.append(('organization', desired_org_states)) - if desired_team_states: - content_types.append(ContentType.objects.get_for_model(Team)) - reconcile_items.append(('team', desired_team_states)) - - if not content_types: - # If both desired states were empty we can simply return because there is nothing to reconcile - return - - # users_roles is a flat set of IDs - users_roles = set(user.roles.filter(content_type__in=content_types).values_list('pk', flat=True)) - - for object_type, desired_states in reconcile_items: - roles = [] - # Get a set of named tuples for the org/team name plus all of the roles we got above - if object_type == 'organization': - for sub_dict in desired_states.values(): - for role_name in sub_dict: - if sub_dict[role_name] is None: - continue - if role_name not in roles: - roles.append(role_name) - model_roles = Organization.objects.filter(name__in=desired_states.keys()).values_list('name', *roles, named=True) - else: - team_names = [] - for teams_dict in desired_states.values(): - team_names.extend(teams_dict.keys()) - for sub_dict in teams_dict.values(): - for role_name in sub_dict: - if sub_dict[role_name] is None: - continue - if role_name not in roles: - roles.append(role_name) - model_roles = Team.objects.filter(name__in=team_names).values_list('name', 'organization__name', *roles, named=True) - - for row in model_roles: - for role_name in roles: - if object_type == 'organization': - desired_state = desired_states.get(row.name, {}) - else: - desired_state = desired_states.get(row.organization__name, {}).get(row.name, {}) - - if desired_state.get(role_name, None) is None: - # The mapping was not defined for this [org/team]/role so we can just pass - continue - - # If somehow the auth adapter knows about an items role but that role is not defined in the DB we are going to print a pretty error - # This is your classic safety net that we should never hit; but here you are reading this comment... good luck and Godspeed. - role_id = getattr(row, role_name, None) - if role_id is None: - logger.error("{} adapter wanted to manage role {} of {} {} but that role is not defined".format(source, role_name, object_type, row.name)) - continue - - if desired_state[role_name]: - # The desired state was the user mapped into the object_type, if the user was not mapped in map them in - if role_id not in users_roles: - logger.debug("{} adapter adding user {} to {} {} as {}".format(source, user.username, object_type, row.name, role_name)) - user.roles.add(role_id) - else: - # The desired state was the user was not mapped into the org, if the user has the permission remove it - if role_id in users_roles: - logger.debug("{} adapter removing user {} permission of {} from {} {}".format(source, user.username, role_name, object_type, row.name)) - user.roles.remove(role_id) - - -def create_org_and_teams(org_list, team_map, adapter, can_create=True): - # - # org_list is a set of organization names - # team_map is a dict of {: } - # - # Move this junk into save of the settings for performance later, there is no need to do that here - # with maybe the exception of someone defining this in settings before the server is started? - # ============================================================================================================== - - if not can_create: - logger.debug(f"Adapter {adapter} is not allowed to create orgs/teams") - return - - # Get all of the IDs and names of orgs in the DB and create any new org defined in LDAP that does not exist in the DB - existing_orgs = get_orgs_by_ids() - - # Parse through orgs and teams provided and create a list of unique items we care about creating - all_orgs = list(set(org_list)) - all_teams = [] - for team_name in team_map: - org_name = team_map[team_name] - if org_name: - if org_name not in all_orgs: - all_orgs.append(org_name) - # We don't have to test if this is in all_teams because team_map is already a hash - all_teams.append(team_name) - else: - # The UI should prevent this condition so this is just a double check to prevent a stack trace.... - # although the rest of the login process might stack later on - logger.error("{} adapter is attempting to create a team {} but it does not have an org".format(adapter, team_name)) - - for org_name in all_orgs: - if org_name and org_name not in existing_orgs: - logger.info("{} adapter is creating org {}".format(adapter, org_name)) - try: - new_org = get_or_create_org_with_default_galaxy_cred(name=org_name) - except IntegrityError: - # Another thread must have created this org before we did so now we need to get it - new_org = get_or_create_org_with_default_galaxy_cred(name=org_name) - # Add the org name to the existing orgs since we created it and we may need it to build the teams below - existing_orgs[org_name] = new_org.id - - # Do the same for teams - existing_team_names = list(Team.objects.all().values_list('name', flat=True)) - for team_name in all_teams: - if team_name not in existing_team_names: - logger.info("{} adapter is creating team {} in org {}".format(adapter, team_name, team_map[team_name])) - try: - Team.objects.create(name=team_name, organization_id=existing_orgs[team_map[team_name]]) - except IntegrityError: - # If another process got here before us that is ok because we don't need the ID from this team or anything - pass - # End move some day - # ============================================================================================================== - - -def get_or_create_org_with_default_galaxy_cred(**kwargs): - from awx.main.models import Organization, Credential - - (org, org_created) = Organization.objects.get_or_create(**kwargs) - if org_created: - logger.debug("Created org {} (id {}) from {}".format(org.name, org.id, kwargs)) - public_galaxy_credential = Credential.objects.filter(managed=True, name='Ansible Galaxy').first() - if public_galaxy_credential is not None: - org.galaxy_credentials.add(public_galaxy_credential) - logger.debug("Added default Ansible Galaxy credential to org") - else: - logger.debug("Could not find default Ansible Galaxy credential to add to org") - return org diff --git a/awx/sso/conf.py b/awx/sso/conf.py deleted file mode 100644 index a835399a3dc7..000000000000 --- a/awx/sso/conf.py +++ /dev/null @@ -1,1614 +0,0 @@ -# Python -import collections -import urllib.parse as urlparse - -# Django -from django.conf import settings -from django.urls import reverse -from django.utils.translation import gettext_lazy as _ - -# Django REST Framework -from rest_framework import serializers - -# AWX -from awx.conf import register, register_validate, fields -from awx.sso.fields import ( - AuthenticationBackendsField, - LDAPConnectionOptionsField, - LDAPDNField, - LDAPDNWithUserField, - LDAPGroupTypeField, - LDAPGroupTypeParamsField, - LDAPOrganizationMapField, - LDAPSearchField, - LDAPSearchUnionField, - LDAPServerURIField, - LDAPTeamMapField, - LDAPUserAttrMapField, - LDAPUserFlagsField, - SAMLContactField, - SAMLEnabledIdPsField, - SAMLOrgAttrField, - SAMLOrgInfoField, - SAMLSecurityField, - SAMLTeamAttrField, - SAMLUserFlagsAttrField, - SocialOrganizationMapField, - SocialTeamMapField, -) -from awx.main.validators import validate_private_key, validate_certificate -from awx.sso.validators import validate_ldap_bind_dn, validate_tacacsplus_disallow_nonascii # noqa - - -class SocialAuthCallbackURL(object): - def __init__(self, provider): - self.provider = provider - - def __call__(self): - path = reverse('social:complete', args=(self.provider,)) - return urlparse.urljoin(settings.TOWER_URL_BASE, path) - - -SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT = _( - '''\ -Mapping to organization admins/users from social auth accounts. This setting -controls which users are placed into which organizations based on their -username and email address. Configuration details are available in the -documentation.\ -''' -) - -# FIXME: /regex/gim (flags) - -SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER = collections.OrderedDict( - [ - ('Default', collections.OrderedDict([('users', True)])), - ('Test Org', collections.OrderedDict([('admins', ['admin@example.com']), ('auditors', ['auditor@example.com']), ('users', True)])), - ( - 'Test Org 2', - collections.OrderedDict( - [ - ('admins', ['admin@example.com', r'/^tower-[^@]+*?@.*$/']), - ('remove_admins', True), - ('users', r'/^[^@].*?@example\.com$/i'), - ('remove_users', True), - ] - ), - ), - ] -) - -SOCIAL_AUTH_TEAM_MAP_HELP_TEXT = _( - '''\ -Mapping of team members (users) from social auth accounts. Configuration -details are available in the documentation.\ -''' -) - -SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER = collections.OrderedDict( - [ - ('My Team', collections.OrderedDict([('organization', 'Test Org'), ('users', [r'/^[^@]+?@test\.example\.com$/']), ('remove', True)])), - ('Other Team', collections.OrderedDict([('organization', 'Test Org 2'), ('users', r'/^[^@]+?@test2\.example\.com$/i'), ('remove', False)])), - ] -) - -############################################################################### -# AUTHENTICATION BACKENDS DYNAMIC SETTING -############################################################################### - -register( - 'AUTHENTICATION_BACKENDS', - field_class=AuthenticationBackendsField, - label=_('Authentication Backends'), - help_text=_('List of authentication backends that are enabled based on ' 'license features and other authentication settings.'), - read_only=True, - depends_on=AuthenticationBackendsField.get_all_required_settings(), - category=_('Authentication'), - category_slug='authentication', -) - -register( - 'SOCIAL_AUTH_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('Social Auth Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('Authentication'), - category_slug='authentication', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('Social Auth Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('Authentication'), - category_slug='authentication', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_USER_FIELDS', - field_class=fields.StringListField, - allow_null=True, - default=None, - label=_('Social Auth User Fields'), - help_text=_( - 'When set to an empty list `[]`, this setting prevents new user ' - 'accounts from being created. Only users who have previously ' - 'logged in using social auth or have a user account with a ' - 'matching email address will be able to login.' - ), - category=_('Authentication'), - category_slug='authentication', - placeholder=['username', 'email'], -) - -############################################################################### -# LDAP AUTHENTICATION SETTINGS -############################################################################### - - -def _register_ldap(append=None): - append_str = '_{}'.format(append) if append else '' - - register( - 'AUTH_LDAP{}_SERVER_URI'.format(append_str), - field_class=LDAPServerURIField, - allow_blank=True, - default='', - label=_('LDAP Server URI'), - help_text=_( - 'URI to connect to LDAP server, such as "ldap://ldap.example.com:389" ' - '(non-SSL) or "ldaps://ldap.example.com:636" (SSL). Multiple LDAP ' - 'servers may be specified by separating with spaces or commas. LDAP ' - 'authentication is disabled if this parameter is empty.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder='ldaps://ldap.example.com:636', - ) - - register( - 'AUTH_LDAP{}_BIND_DN'.format(append_str), - field_class=fields.CharField, - allow_blank=True, - default='', - validators=[validate_ldap_bind_dn], - label=_('LDAP Bind DN'), - help_text=_( - 'DN (Distinguished Name) of user to bind for all search queries. This' - ' is the system user account we will use to login to query LDAP for other' - ' user information. Refer to the documentation for example syntax.' - ), - category=_('LDAP'), - category_slug='ldap', - ) - - register( - 'AUTH_LDAP{}_BIND_PASSWORD'.format(append_str), - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('LDAP Bind Password'), - help_text=_('Password used to bind LDAP user account.'), - category=_('LDAP'), - category_slug='ldap', - encrypted=True, - ) - - register( - 'AUTH_LDAP{}_START_TLS'.format(append_str), - field_class=fields.BooleanField, - default=False, - label=_('LDAP Start TLS'), - help_text=_('Whether to enable TLS when the LDAP connection is not using SSL.'), - category=_('LDAP'), - category_slug='ldap', - ) - - register( - 'AUTH_LDAP{}_CONNECTION_OPTIONS'.format(append_str), - field_class=LDAPConnectionOptionsField, - default={'OPT_REFERRALS': 0, 'OPT_NETWORK_TIMEOUT': 30}, - label=_('LDAP Connection Options'), - help_text=_( - 'Additional options to set for the LDAP connection. LDAP ' - 'referrals are disabled by default (to prevent certain LDAP ' - 'queries from hanging with AD). Option names should be strings ' - '(e.g. "OPT_REFERRALS"). Refer to ' - 'https://www.python-ldap.org/doc/html/ldap.html#options for ' - 'possible options and values that can be set.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder=collections.OrderedDict([('OPT_REFERRALS', 0), ('OPT_NETWORK_TIMEOUT', 30)]), - ) - - register( - 'AUTH_LDAP{}_USER_SEARCH'.format(append_str), - field_class=LDAPSearchUnionField, - default=[], - label=_('LDAP User Search'), - help_text=_( - 'LDAP search query to find users. Any user that matches the given ' - 'pattern will be able to login to the service. The user should also be ' - 'mapped into an organization (as defined in the ' - 'AUTH_LDAP_ORGANIZATION_MAP setting). If multiple search queries ' - 'need to be supported use of "LDAPUnion" is possible. See ' - 'the documentation for details.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder=('OU=Users,DC=example,DC=com', 'SCOPE_SUBTREE', '(sAMAccountName=%(user)s)'), - ) - - register( - 'AUTH_LDAP{}_USER_DN_TEMPLATE'.format(append_str), - field_class=LDAPDNWithUserField, - allow_blank=True, - allow_null=True, - default=None, - label=_('LDAP User DN Template'), - help_text=_( - 'Alternative to user search, if user DNs are all of the same ' - 'format. This approach is more efficient for user lookups than ' - 'searching if it is usable in your organizational environment. If ' - 'this setting has a value it will be used instead of ' - 'AUTH_LDAP_USER_SEARCH.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder='uid=%(user)s,OU=Users,DC=example,DC=com', - ) - - register( - 'AUTH_LDAP{}_USER_ATTR_MAP'.format(append_str), - field_class=LDAPUserAttrMapField, - default={}, - label=_('LDAP User Attribute Map'), - help_text=_( - 'Mapping of LDAP user schema to API user attributes. The default' - ' setting is valid for ActiveDirectory but users with other LDAP' - ' configurations may need to change the values. Refer to the' - ' documentation for additional details.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder=collections.OrderedDict([('first_name', 'givenName'), ('last_name', 'sn'), ('email', 'mail')]), - ) - - register( - 'AUTH_LDAP{}_GROUP_SEARCH'.format(append_str), - field_class=LDAPSearchField, - default=[], - label=_('LDAP Group Search'), - help_text=_( - 'Users are mapped to organizations based on their membership in LDAP' - ' groups. This setting defines the LDAP search query to find groups. ' - 'Unlike the user search, group search does not support LDAPSearchUnion.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder=('DC=example,DC=com', 'SCOPE_SUBTREE', '(objectClass=group)'), - ) - - register( - 'AUTH_LDAP{}_GROUP_TYPE'.format(append_str), - field_class=LDAPGroupTypeField, - label=_('LDAP Group Type'), - help_text=_( - 'The group type may need to be changed based on the type of the ' - 'LDAP server. Values are listed at: ' - 'https://django-auth-ldap.readthedocs.io/en/stable/groups.html#types-of-groups' - ), - category=_('LDAP'), - category_slug='ldap', - default='MemberDNGroupType', - depends_on=['AUTH_LDAP{}_GROUP_TYPE_PARAMS'.format(append_str)], - ) - - register( - 'AUTH_LDAP{}_GROUP_TYPE_PARAMS'.format(append_str), - field_class=LDAPGroupTypeParamsField, - label=_('LDAP Group Type Parameters'), - help_text=_('Key value parameters to send the chosen group type init method.'), - category=_('LDAP'), - category_slug='ldap', - default=collections.OrderedDict([('member_attr', 'member'), ('name_attr', 'cn')]), - placeholder=collections.OrderedDict([('ldap_group_user_attr', 'legacyuid'), ('member_attr', 'member'), ('name_attr', 'cn')]), - depends_on=['AUTH_LDAP{}_GROUP_TYPE'.format(append_str)], - ) - - register( - 'AUTH_LDAP{}_REQUIRE_GROUP'.format(append_str), - field_class=LDAPDNField, - allow_blank=True, - allow_null=True, - default=None, - label=_('LDAP Require Group'), - help_text=_( - 'Group DN required to login. If specified, user must be a member ' - 'of this group to login via LDAP. If not set, everyone in LDAP ' - 'that matches the user search will be able to login to the service. ' - 'Only one require group is supported.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder='CN=Service Users,OU=Users,DC=example,DC=com', - ) - - register( - 'AUTH_LDAP{}_DENY_GROUP'.format(append_str), - field_class=LDAPDNField, - allow_blank=True, - allow_null=True, - default=None, - label=_('LDAP Deny Group'), - help_text=_( - 'Group DN denied from login. If specified, user will not be ' 'allowed to login if a member of this group. Only one deny group ' 'is supported.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder='CN=Disabled Users,OU=Users,DC=example,DC=com', - ) - - register( - 'AUTH_LDAP{}_USER_FLAGS_BY_GROUP'.format(append_str), - field_class=LDAPUserFlagsField, - default={}, - label=_('LDAP User Flags By Group'), - help_text=_( - 'Retrieve users from a given group. At this time, superuser and system' - ' auditors are the only groups supported. Refer to the' - ' documentation for more detail.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder=collections.OrderedDict( - [('is_superuser', 'CN=Domain Admins,CN=Users,DC=example,DC=com'), ('is_system_auditor', 'CN=Domain Auditors,CN=Users,DC=example,DC=com')] - ), - ) - - register( - 'AUTH_LDAP{}_ORGANIZATION_MAP'.format(append_str), - field_class=LDAPOrganizationMapField, - default={}, - label=_('LDAP Organization Map'), - help_text=_( - 'Mapping between organization admins/users and LDAP groups. This ' - 'controls which users are placed into which organizations ' - 'relative to their LDAP group memberships. Configuration details ' - 'are available in the documentation.' - ), - category=_('LDAP'), - category_slug='ldap', - placeholder=collections.OrderedDict( - [ - ( - 'Test Org', - collections.OrderedDict( - [ - ('admins', 'CN=Domain Admins,CN=Users,DC=example,DC=com'), - ('auditors', 'CN=Domain Auditors,CN=Users,DC=example,DC=com'), - ('users', ['CN=Domain Users,CN=Users,DC=example,DC=com']), - ('remove_users', True), - ('remove_admins', True), - ] - ), - ), - ( - 'Test Org 2', - collections.OrderedDict( - [('admins', 'CN=Administrators,CN=Builtin,DC=example,DC=com'), ('users', True), ('remove_users', True), ('remove_admins', True)] - ), - ), - ] - ), - ) - - register( - 'AUTH_LDAP{}_TEAM_MAP'.format(append_str), - field_class=LDAPTeamMapField, - default={}, - label=_('LDAP Team Map'), - help_text=_('Mapping between team members (users) and LDAP groups. Configuration' ' details are available in the documentation.'), - category=_('LDAP'), - category_slug='ldap', - placeholder=collections.OrderedDict( - [ - ( - 'My Team', - collections.OrderedDict([('organization', 'Test Org'), ('users', ['CN=Domain Users,CN=Users,DC=example,DC=com']), ('remove', True)]), - ), - ( - 'Other Team', - collections.OrderedDict([('organization', 'Test Org 2'), ('users', 'CN=Other Users,CN=Users,DC=example,DC=com'), ('remove', False)]), - ), - ] - ), - ) - - -_register_ldap() -_register_ldap('1') -_register_ldap('2') -_register_ldap('3') -_register_ldap('4') -_register_ldap('5') - -############################################################################### -# RADIUS AUTHENTICATION SETTINGS -############################################################################### - -register( - 'RADIUS_SERVER', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('RADIUS Server'), - help_text=_('Hostname/IP of RADIUS server. RADIUS authentication is ' 'disabled if this setting is empty.'), - category=_('RADIUS'), - category_slug='radius', - placeholder='radius.example.com', -) - -register( - 'RADIUS_PORT', - field_class=fields.IntegerField, - min_value=1, - max_value=65535, - default=1812, - label=_('RADIUS Port'), - help_text=_('Port of RADIUS server.'), - category=_('RADIUS'), - category_slug='radius', -) - -register( - 'RADIUS_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('RADIUS Secret'), - help_text=_('Shared secret for authenticating to RADIUS server.'), - category=_('RADIUS'), - category_slug='radius', - encrypted=True, -) - -############################################################################### -# TACACSPLUS AUTHENTICATION SETTINGS -############################################################################### - -register( - 'TACACSPLUS_HOST', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('TACACS+ Server'), - help_text=_('Hostname of TACACS+ server.'), - category=_('TACACS+'), - category_slug='tacacsplus', -) - -register( - 'TACACSPLUS_PORT', - field_class=fields.IntegerField, - min_value=1, - max_value=65535, - default=49, - label=_('TACACS+ Port'), - help_text=_('Port number of TACACS+ server.'), - category=_('TACACS+'), - category_slug='tacacsplus', -) - -register( - 'TACACSPLUS_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - validators=[validate_tacacsplus_disallow_nonascii], - label=_('TACACS+ Secret'), - help_text=_('Shared secret for authenticating to TACACS+ server.'), - category=_('TACACS+'), - category_slug='tacacsplus', - encrypted=True, -) - -register( - 'TACACSPLUS_SESSION_TIMEOUT', - field_class=fields.IntegerField, - min_value=0, - default=5, - label=_('TACACS+ Auth Session Timeout'), - help_text=_('TACACS+ session timeout value in seconds, 0 disables timeout.'), - category=_('TACACS+'), - category_slug='tacacsplus', - unit=_('seconds'), -) - -register( - 'TACACSPLUS_AUTH_PROTOCOL', - field_class=fields.ChoiceField, - choices=['ascii', 'pap'], - default='ascii', - label=_('TACACS+ Authentication Protocol'), - help_text=_('Choose the authentication protocol used by TACACS+ client.'), - category=_('TACACS+'), - category_slug='tacacsplus', -) - -############################################################################### -# GOOGLE OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('google-oauth2'), - label=_('Google OAuth2 Callback URL'), - help_text=_( - 'Provide this URL as the callback URL for your application as part ' 'of your registration process. Refer to the ' 'documentation for more detail.' - ), - category=_('Google OAuth2'), - category_slug='google-oauth2', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('Google OAuth2 Key'), - help_text=_('The OAuth2 key from your web application.'), - category=_('Google OAuth2'), - category_slug='google-oauth2', - placeholder='528620852399-gm2dt4hrl2tsj67fqamk09k1e0ad6gd8.apps.googleusercontent.com', -) - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('Google OAuth2 Secret'), - help_text=_('The OAuth2 secret from your web application.'), - category=_('Google OAuth2'), - category_slug='google-oauth2', - placeholder='q2fMVCmEregbg-drvebPp8OW', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS', - field_class=fields.StringListField, - default=[], - label=_('Google OAuth2 Allowed Domains'), - help_text=_('Update this setting to restrict the domains who are allowed to ' 'login using Google OAuth2.'), - category=_('Google OAuth2'), - category_slug='google-oauth2', - placeholder=['example.com'], -) - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS', - field_class=fields.DictField, - default={}, - label=_('Google OAuth2 Extra Arguments'), - help_text=_( - 'Extra arguments for Google OAuth2 login. You can restrict it to' - ' only allow a single domain to authenticate, even if the user is' - ' logged in with multple Google accounts. Refer to the' - ' documentation for more detail.' - ), - category=_('Google OAuth2'), - category_slug='google-oauth2', - placeholder={'hd': 'example.com'}, -) - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('Google OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('Google OAuth2'), - category_slug='google-oauth2', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GOOGLE_OAUTH2_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('Google OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('Google OAuth2'), - category_slug='google-oauth2', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# GITHUB OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GITHUB_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('github'), - label=_('GitHub OAuth2 Callback URL'), - help_text=_( - 'Provide this URL as the callback URL for your application as part ' 'of your registration process. Refer to the ' 'documentation for more detail.' - ), - category=_('GitHub OAuth2'), - category_slug='github', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GITHUB_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your GitHub developer application.'), - category=_('GitHub OAuth2'), - category_slug='github', -) - -register( - 'SOCIAL_AUTH_GITHUB_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your GitHub developer application.'), - category=_('GitHub OAuth2'), - category_slug='github', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('GitHub OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('GitHub OAuth2'), - category_slug='github', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('GitHub OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('GitHub OAuth2'), - category_slug='github', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# GITHUB ORG OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GITHUB_ORG_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('github-org'), - label=_('GitHub Organization OAuth2 Callback URL'), - help_text=_( - 'Provide this URL as the callback URL for your application as part ' 'of your registration process. Refer to the ' 'documentation for more detail.' - ), - category=_('GitHub Organization OAuth2'), - category_slug='github-org', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GITHUB_ORG_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Organization OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your GitHub organization application.'), - category=_('GitHub Organization OAuth2'), - category_slug='github-org', -) - -register( - 'SOCIAL_AUTH_GITHUB_ORG_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Organization OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your GitHub organization application.'), - category=_('GitHub Organization OAuth2'), - category_slug='github-org', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GITHUB_ORG_NAME', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Organization Name'), - help_text=_('The name of your GitHub organization, as used in your ' 'organization\'s URL: https://github.com//.'), - category=_('GitHub Organization OAuth2'), - category_slug='github-org', -) - -register( - 'SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('GitHub Organization OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('GitHub Organization OAuth2'), - category_slug='github-org', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('GitHub Organization OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('GitHub Organization OAuth2'), - category_slug='github-org', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# GITHUB TEAM OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('github-team'), - label=_('GitHub Team OAuth2 Callback URL'), - help_text=_( - 'Create an organization-owned application at ' - 'https://github.com/organizations//settings/applications ' - 'and obtain an OAuth2 key (Client ID) and secret (Client Secret). ' - 'Provide this URL as the callback URL for your application.' - ), - category=_('GitHub Team OAuth2'), - category_slug='github-team', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Team OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your GitHub organization application.'), - category=_('GitHub Team OAuth2'), - category_slug='github-team', -) - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Team OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your GitHub organization application.'), - category=_('GitHub Team OAuth2'), - category_slug='github-team', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_ID', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Team ID'), - help_text=_('Find the numeric team ID using the Github API: ' 'http://fabian-kostadinov.github.io/2015/01/16/how-to-find-a-github-team-id/.'), - category=_('GitHub Team OAuth2'), - category_slug='github-team', -) - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('GitHub Team OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('GitHub Team OAuth2'), - category_slug='github-team', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('GitHub Team OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('GitHub Team OAuth2'), - category_slug='github-team', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# GITHUB ENTERPRISE OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('github-enterprise'), - label=_('GitHub Enterprise OAuth2 Callback URL'), - help_text=_( - 'Provide this URL as the callback URL for your application as part ' 'of your registration process. Refer to the ' 'documentation for more detail.' - ), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise URL'), - help_text=_('The URL for your Github Enterprise instance, e.g.: http(s)://hostname/. Refer to Github Enterprise ' 'documentation for more details.'), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise API URL'), - help_text=_( - 'The API URL for your GitHub Enterprise instance, e.g.: http(s)://hostname/api/v3/. Refer to Github ' 'Enterprise documentation for more details.' - ), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your GitHub Enterprise developer application.'), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your GitHub Enterprise developer application.'), - category=_('GitHub OAuth2'), - category_slug='github-enterprise', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('GitHub Enterprise OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('GitHub Enterprise OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# GITHUB ENTERPRISE ORG OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('github-enterprise-org'), - label=_('GitHub Enterprise Organization OAuth2 Callback URL'), - help_text=_( - 'Provide this URL as the callback URL for your application as part ' 'of your registration process. Refer to the ' 'documentation for more detail.' - ), - category=_('GitHub Enterprise Organization OAuth2'), - category_slug='github-enterprise-org', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Organization URL'), - help_text=_('The URL for your Github Enterprise instance, e.g.: http(s)://hostname/. Refer to Github Enterprise ' 'documentation for more details.'), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise-org', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Organization API URL'), - help_text=_( - 'The API URL for your GitHub Enterprise instance, e.g.: http(s)://hostname/api/v3/. Refer to Github ' 'Enterprise documentation for more details.' - ), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise-org', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Organization OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your GitHub Enterprise organization application.'), - category=_('GitHub Enterprise Organization OAuth2'), - category_slug='github-enterprise-org', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Organization OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your GitHub Enterprise organization application.'), - category=_('GitHub Enterprise Organization OAuth2'), - category_slug='github-enterprise-org', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Organization Name'), - help_text=_('The name of your GitHub Enterprise organization, as used in your ' 'organization\'s URL: https://github.com//.'), - category=_('GitHub Enterprise Organization OAuth2'), - category_slug='github-enterprise-org', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('GitHub Enterprise Organization OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('GitHub Enterprise Organization OAuth2'), - category_slug='github-enterprise-org', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('GitHub Enterprise Organization OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('GitHub Enterprise Organization OAuth2'), - category_slug='github-enterprise-org', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# GITHUB ENTERPRISE TEAM OAUTH2 AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('github-enterprise-team'), - label=_('GitHub Enterprise Team OAuth2 Callback URL'), - help_text=_( - 'Create an organization-owned application at ' - 'https://github.com/organizations//settings/applications ' - 'and obtain an OAuth2 key (Client ID) and secret (Client Secret). ' - 'Provide this URL as the callback URL for your application.' - ), - category=_('GitHub Enterprise Team OAuth2'), - category_slug='github-enterprise-team', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Team URL'), - help_text=_('The URL for your Github Enterprise instance, e.g.: http(s)://hostname/. Refer to Github Enterprise ' 'documentation for more details.'), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise-team', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Team API URL'), - help_text=_( - 'The API URL for your GitHub Enterprise instance, e.g.: http(s)://hostname/api/v3/. Refer to Github ' 'Enterprise documentation for more details.' - ), - category=_('GitHub Enterprise OAuth2'), - category_slug='github-enterprise-team', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Team OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your GitHub Enterprise organization application.'), - category=_('GitHub Enterprise Team OAuth2'), - category_slug='github-enterprise-team', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Team OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your GitHub Enterprise organization application.'), - category=_('GitHub Enterprise Team OAuth2'), - category_slug='github-enterprise-team', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('GitHub Enterprise Team ID'), - help_text=_('Find the numeric team ID using the Github Enterprise API: ' 'http://fabian-kostadinov.github.io/2015/01/16/how-to-find-a-github-team-id/.'), - category=_('GitHub Enterprise Team OAuth2'), - category_slug='github-enterprise-team', -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('GitHub Enterprise Team OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('GitHub Enterprise Team OAuth2'), - category_slug='github-enterprise-team', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('GitHub Enterprise Team OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('GitHub Enterprise Team OAuth2'), - category_slug='github-enterprise-team', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# MICROSOFT AZURE ACTIVE DIRECTORY SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_AZUREAD_OAUTH2_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('azuread-oauth2'), - label=_('Azure AD OAuth2 Callback URL'), - help_text=_( - 'Provide this URL as the callback URL for your application as part' ' of your registration process. Refer to the' ' documentation for more detail. ' - ), - category=_('Azure AD OAuth2'), - category_slug='azuread-oauth2', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_AZUREAD_OAUTH2_KEY', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('Azure AD OAuth2 Key'), - help_text=_('The OAuth2 key (Client ID) from your Azure AD application.'), - category=_('Azure AD OAuth2'), - category_slug='azuread-oauth2', -) - -register( - 'SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('Azure AD OAuth2 Secret'), - help_text=_('The OAuth2 secret (Client Secret) from your Azure AD application.'), - category=_('Azure AD OAuth2'), - category_slug='azuread-oauth2', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_AZUREAD_OAUTH2_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('Azure AD OAuth2 Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('Azure AD OAuth2'), - category_slug='azuread-oauth2', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_AZUREAD_OAUTH2_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('Azure AD OAuth2 Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('Azure AD OAuth2'), - category_slug='azuread-oauth2', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -############################################################################### -# Generic OIDC AUTHENTICATION SETTINGS -############################################################################### - -register( - 'SOCIAL_AUTH_OIDC_KEY', - field_class=fields.CharField, - allow_null=False, - default=None, - label=_('OIDC Key'), - help_text='The OIDC key (Client ID) from your IDP.', - category=_('Generic OIDC'), - category_slug='oidc', -) - -register( - 'SOCIAL_AUTH_OIDC_SECRET', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('OIDC Secret'), - help_text=_('The OIDC secret (Client Secret) from your IDP.'), - category=_('Generic OIDC'), - category_slug='oidc', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_OIDC_OIDC_ENDPOINT', - field_class=fields.CharField, - allow_blank=True, - default='', - label=_('OIDC Provider URL'), - help_text=_('The URL for your OIDC provider including the path up to /.well-known/openid-configuration'), - category=_('Generic OIDC'), - category_slug='oidc', -) - -register( - 'SOCIAL_AUTH_OIDC_VERIFY_SSL', - field_class=fields.BooleanField, - default=True, - label=_('Verify OIDC Provider Certificate'), - help_text=_('Verify the OIDV provider ssl certificate.'), - category=_('Generic OIDC'), - category_slug='oidc', -) - -############################################################################### -# SAML AUTHENTICATION SETTINGS -############################################################################### - - -def get_saml_metadata_url(): - return urlparse.urljoin(settings.TOWER_URL_BASE, reverse('sso:saml_metadata')) - - -def get_saml_entity_id(): - return settings.TOWER_URL_BASE - - -register( - 'SAML_AUTO_CREATE_OBJECTS', - field_class=fields.BooleanField, - default=True, - label=_('Automatically Create Organizations and Teams on SAML Login'), - help_text=_('When enabled (the default), mapped Organizations and Teams ' 'will be created automatically on successful SAML login.'), - category=_('SAML'), - category_slug='saml', -) - -register( - 'SOCIAL_AUTH_SAML_CALLBACK_URL', - field_class=fields.CharField, - read_only=True, - default=SocialAuthCallbackURL('saml'), - label=_('SAML Assertion Consumer Service (ACS) URL'), - help_text=_( - 'Register the service as a service provider (SP) with each identity ' - 'provider (IdP) you have configured. Provide your SP Entity ID ' - 'and this ACS URL for your application.' - ), - category=_('SAML'), - category_slug='saml', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_SAML_METADATA_URL', - field_class=fields.CharField, - read_only=True, - default=get_saml_metadata_url, - label=_('SAML Service Provider Metadata URL'), - help_text=_('If your identity provider (IdP) allows uploading an XML ' 'metadata file, you can download one from this URL.'), - category=_('SAML'), - category_slug='saml', -) - -register( - 'SOCIAL_AUTH_SAML_SP_ENTITY_ID', - field_class=fields.CharField, - allow_blank=True, - default=get_saml_entity_id, - label=_('SAML Service Provider Entity ID'), - help_text=_( - 'The application-defined unique identifier used as the ' - 'audience of the SAML service provider (SP) configuration. ' - 'This is usually the URL for the service.' - ), - category=_('SAML'), - category_slug='saml', - depends_on=['TOWER_URL_BASE'], -) - -register( - 'SOCIAL_AUTH_SAML_SP_PUBLIC_CERT', - field_class=fields.CharField, - allow_blank=True, - required=True, - validators=[validate_certificate], - label=_('SAML Service Provider Public Certificate'), - help_text=_('Create a keypair to use as a service provider (SP) ' 'and include the certificate content here.'), - category=_('SAML'), - category_slug='saml', -) - -register( - 'SOCIAL_AUTH_SAML_SP_PRIVATE_KEY', - field_class=fields.CharField, - allow_blank=True, - required=True, - validators=[validate_private_key], - label=_('SAML Service Provider Private Key'), - help_text=_('Create a keypair to use as a service provider (SP) ' 'and include the private key content here.'), - category=_('SAML'), - category_slug='saml', - encrypted=True, -) - -register( - 'SOCIAL_AUTH_SAML_ORG_INFO', - field_class=SAMLOrgInfoField, - required=True, - label=_('SAML Service Provider Organization Info'), - help_text=_('Provide the URL, display name, and the name of your app. Refer to' ' the documentation for example syntax.'), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict( - [('en-US', collections.OrderedDict([('name', 'example'), ('displayname', 'Example'), ('url', 'http://www.example.com')]))] - ), -) - -register( - 'SOCIAL_AUTH_SAML_TECHNICAL_CONTACT', - field_class=SAMLContactField, - allow_blank=True, - required=True, - label=_('SAML Service Provider Technical Contact'), - help_text=_('Provide the name and email address of the technical contact for' ' your service provider. Refer to the documentation' ' for example syntax.'), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict([('givenName', 'Technical Contact'), ('emailAddress', 'techsup@example.com')]), -) - -register( - 'SOCIAL_AUTH_SAML_SUPPORT_CONTACT', - field_class=SAMLContactField, - allow_blank=True, - required=True, - label=_('SAML Service Provider Support Contact'), - help_text=_('Provide the name and email address of the support contact for your' ' service provider. Refer to the documentation for' ' example syntax.'), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict([('givenName', 'Support Contact'), ('emailAddress', 'support@example.com')]), -) - -register( - 'SOCIAL_AUTH_SAML_ENABLED_IDPS', - field_class=SAMLEnabledIdPsField, - default={}, - label=_('SAML Enabled Identity Providers'), - help_text=_( - 'Configure the Entity ID, SSO URL and certificate for each identity' - ' provider (IdP) in use. Multiple SAML IdPs are supported. Some IdPs' - ' may provide user data using attribute names that differ from the' - ' default OIDs. Attribute names may be overridden for each IdP. Refer' - ' to the Ansible documentation for additional details and syntax.' - ), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict( - [ - ( - 'Okta', - collections.OrderedDict( - [ - ('entity_id', 'http://www.okta.com/HHniyLkaxk9e76wD0Thh'), - ('url', 'https://dev-123456.oktapreview.com/app/ansibletower/HHniyLkaxk9e76wD0Thh/sso/saml'), - ('x509cert', 'MIIDpDCCAoygAwIBAgIGAVVZ4rPzMA0GCSqGSIb3...'), - ('attr_user_permanent_id', 'username'), - ('attr_first_name', 'first_name'), - ('attr_last_name', 'last_name'), - ('attr_username', 'username'), - ('attr_email', 'email'), - ] - ), - ), - ( - 'OneLogin', - collections.OrderedDict( - [ - ('entity_id', 'https://app.onelogin.com/saml/metadata/123456'), - ('url', 'https://example.onelogin.com/trust/saml2/http-post/sso/123456'), - ('x509cert', 'MIIEJjCCAw6gAwIBAgIUfuSD54OPSBhndDHh3gZo...'), - ('attr_user_permanent_id', 'name_id'), - ('attr_first_name', 'User.FirstName'), - ('attr_last_name', 'User.LastName'), - ('attr_username', 'User.email'), - ('attr_email', 'User.email'), - ] - ), - ), - ] - ), -) - -register( - 'SOCIAL_AUTH_SAML_SECURITY_CONFIG', - field_class=SAMLSecurityField, - allow_null=True, - default={'requestedAuthnContext': False}, - label=_('SAML Security Config'), - help_text=_( - 'A dict of key value pairs that are passed to the underlying' ' python-saml security setting' ' https://github.com/onelogin/python-saml#settings' - ), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict( - [ - ("nameIdEncrypted", False), - ("authnRequestsSigned", False), - ("logoutRequestSigned", False), - ("logoutResponseSigned", False), - ("signMetadata", False), - ("wantMessagesSigned", False), - ("wantAssertionsSigned", False), - ("wantAssertionsEncrypted", False), - ("wantNameId", True), - ("wantNameIdEncrypted", False), - ("wantAttributeStatement", True), - ("requestedAuthnContext", True), - ("requestedAuthnContextComparison", "exact"), - ("metadataValidUntil", "2015-06-26T20:00:00Z"), - ("metadataCacheDuration", "PT518400S"), - ("signatureAlgorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"), - ("digestAlgorithm", "http://www.w3.org/2000/09/xmldsig#sha1"), - ] - ), -) - -register( - 'SOCIAL_AUTH_SAML_SP_EXTRA', - field_class=fields.DictField, - allow_null=True, - default=None, - label=_('SAML Service Provider extra configuration data'), - help_text=_('A dict of key value pairs to be passed to the underlying' ' python-saml Service Provider configuration setting.'), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict(), -) - -register( - 'SOCIAL_AUTH_SAML_EXTRA_DATA', - field_class=fields.ListTuplesField, - allow_null=True, - default=None, - label=_('SAML IDP to extra_data attribute mapping'), - help_text=_('A list of tuples that maps IDP attributes to extra_attributes.' ' Each attribute will be a list of values, even if only 1 value.'), - category=_('SAML'), - category_slug='saml', - placeholder=[('attribute_name', 'extra_data_name_for_attribute'), ('department', 'department'), ('manager_full_name', 'manager_full_name')], -) - -register( - 'SOCIAL_AUTH_SAML_ORGANIZATION_MAP', - field_class=SocialOrganizationMapField, - allow_null=True, - default=None, - label=_('SAML Organization Map'), - help_text=SOCIAL_AUTH_ORGANIZATION_MAP_HELP_TEXT, - category=_('SAML'), - category_slug='saml', - placeholder=SOCIAL_AUTH_ORGANIZATION_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_SAML_TEAM_MAP', - field_class=SocialTeamMapField, - allow_null=True, - default=None, - label=_('SAML Team Map'), - help_text=SOCIAL_AUTH_TEAM_MAP_HELP_TEXT, - category=_('SAML'), - category_slug='saml', - placeholder=SOCIAL_AUTH_TEAM_MAP_PLACEHOLDER, -) - -register( - 'SOCIAL_AUTH_SAML_ORGANIZATION_ATTR', - field_class=SAMLOrgAttrField, - allow_null=True, - default=None, - label=_('SAML Organization Attribute Mapping'), - help_text=_('Used to translate user organization membership.'), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict( - [ - ('saml_attr', 'organization'), - ('saml_admin_attr', 'organization_admin'), - ('saml_auditor_attr', 'organization_auditor'), - ('remove', True), - ('remove_admins', True), - ('remove_auditors', True), - ] - ), -) - -register( - 'SOCIAL_AUTH_SAML_TEAM_ATTR', - field_class=SAMLTeamAttrField, - allow_null=True, - default=None, - label=_('SAML Team Attribute Mapping'), - help_text=_('Used to translate user team membership.'), - category=_('SAML'), - category_slug='saml', - placeholder=collections.OrderedDict( - [ - ('saml_attr', 'team'), - ('remove', True), - ( - 'team_org_map', - [ - collections.OrderedDict([('team', 'Marketing'), ('organization', 'Red Hat')]), - collections.OrderedDict([('team', 'Human Resources'), ('organization', 'Red Hat')]), - collections.OrderedDict([('team', 'Engineering'), ('organization', 'Red Hat')]), - collections.OrderedDict([('team', 'Engineering'), ('organization', 'Ansible')]), - collections.OrderedDict([('team', 'Quality Engineering'), ('organization', 'Ansible')]), - collections.OrderedDict([('team', 'Sales'), ('organization', 'Ansible')]), - ], - ), - ] - ), -) - -register( - 'SOCIAL_AUTH_SAML_USER_FLAGS_BY_ATTR', - field_class=SAMLUserFlagsAttrField, - allow_null=True, - default=None, - label=_('SAML User Flags Attribute Mapping'), - help_text=_('Used to map super users and system auditors from SAML.'), - category=_('SAML'), - category_slug='saml', - placeholder=[ - ('is_superuser_attr', 'saml_attr'), - ('is_superuser_value', ['value']), - ('is_superuser_role', ['saml_role']), - ('remove_superusers', True), - ('is_system_auditor_attr', 'saml_attr'), - ('is_system_auditor_value', ['value']), - ('is_system_auditor_role', ['saml_role']), - ('remove_system_auditors', True), - ], -) - - -def tacacs_validate(serializer, attrs): - if not serializer.instance or not hasattr(serializer.instance, 'TACACSPLUS_HOST') or not hasattr(serializer.instance, 'TACACSPLUS_SECRET'): - return attrs - errors = [] - host = serializer.instance.TACACSPLUS_HOST - if 'TACACSPLUS_HOST' in attrs: - host = attrs['TACACSPLUS_HOST'] - secret = serializer.instance.TACACSPLUS_SECRET - if 'TACACSPLUS_SECRET' in attrs: - secret = attrs['TACACSPLUS_SECRET'] - if host and not secret: - errors.append('TACACSPLUS_SECRET is required when TACACSPLUS_HOST is provided.') - if errors: - raise serializers.ValidationError(_('\n'.join(errors))) - return attrs - - -register_validate('tacacsplus', tacacs_validate) diff --git a/awx/sso/fields.py b/awx/sso/fields.py deleted file mode 100644 index 25b7f2c304a6..000000000000 --- a/awx/sso/fields.py +++ /dev/null @@ -1,725 +0,0 @@ -import collections -import copy -import inspect -import json -import re - -import six - -# Python LDAP -import ldap -import awx - -# Django -from django.utils.translation import gettext_lazy as _ - -# Django Auth LDAP -import django_auth_ldap.config -from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion - -from rest_framework.exceptions import ValidationError -from rest_framework.fields import empty, Field, SkipField - -# This must be imported so get_subclasses picks it up -from awx.sso.ldap_group_types import PosixUIDGroupType # noqa - -# AWX -from awx.conf import fields -from awx.main.validators import validate_certificate -from awx.sso.validators import ( # noqa - validate_ldap_dn, - validate_ldap_bind_dn, - validate_ldap_dn_with_user, - validate_ldap_filter, - validate_ldap_filter_with_user, - validate_tacacsplus_disallow_nonascii, -) - - -def get_subclasses(cls): - for subclass in cls.__subclasses__(): - for subsubclass in get_subclasses(subclass): - yield subsubclass - yield subclass - - -def find_class_in_modules(class_name): - """ - Used to find ldap subclasses by string - """ - module_search_space = [django_auth_ldap.config, awx.sso.ldap_group_types] - for m in module_search_space: - cls = getattr(m, class_name, None) - if cls: - return cls - return None - - -class DependsOnMixin: - def get_depends_on(self): - """ - Get the value of the dependent field. - First try to find the value in the request. - Then fall back to the raw value from the setting in the DB. - """ - from django.conf import settings - - dependent_key = next(iter(self.depends_on)) - - if self.context: - request = self.context.get('request', None) - if request and request.data and request.data.get(dependent_key, None): - return request.data.get(dependent_key) - res = settings._get_local(dependent_key, validate=False) - return res - - -class _Forbidden(Field): - default_error_messages = {'invalid': _('Invalid field.')} - - def run_validation(self, value): - self.fail('invalid') - - -class HybridDictField(fields.DictField): - """A DictField, but with defined fixed Fields for certain keys.""" - - def __init__(self, *args, **kwargs): - self.allow_blank = kwargs.pop('allow_blank', False) - - fields = [ - sorted( - ((field_name, obj) for field_name, obj in cls.__dict__.items() if isinstance(obj, Field) and field_name != 'child'), - key=lambda x: x[1]._creation_counter, - ) - for cls in reversed(self.__class__.__mro__) - ] - self._declared_fields = collections.OrderedDict(f for group in fields for f in group) - - super().__init__(*args, **kwargs) - - def to_representation(self, value): - fields = copy.deepcopy(self._declared_fields) - return { - key: field.to_representation(val) if val is not None else None - for key, val, field in ((six.text_type(key), val, fields.get(key, self.child)) for key, val in value.items()) - if not field.write_only - } - - def run_child_validation(self, data): - result = {} - - if not data and self.allow_blank: - return result - - errors = collections.OrderedDict() - fields = copy.deepcopy(self._declared_fields) - keys = set(fields.keys()) | set(data.keys()) - - for key in keys: - value = data.get(key, empty) - key = six.text_type(key) - field = fields.get(key, self.child) - try: - if field.read_only: - continue # Ignore read_only fields, as Serializer seems to do. - result[key] = field.run_validation(value) - except ValidationError as e: - errors[key] = e.detail - except SkipField: - pass - - if not errors: - return result - raise ValidationError(errors) - - -class AuthenticationBackendsField(fields.StringListField): - # Mapping of settings that must be set in order to enable each - # authentication backend. - REQUIRED_BACKEND_SETTINGS = collections.OrderedDict( - [ - ('awx.sso.backends.LDAPBackend', ['AUTH_LDAP_SERVER_URI']), - ('awx.sso.backends.LDAPBackend1', ['AUTH_LDAP_1_SERVER_URI']), - ('awx.sso.backends.LDAPBackend2', ['AUTH_LDAP_2_SERVER_URI']), - ('awx.sso.backends.LDAPBackend3', ['AUTH_LDAP_3_SERVER_URI']), - ('awx.sso.backends.LDAPBackend4', ['AUTH_LDAP_4_SERVER_URI']), - ('awx.sso.backends.LDAPBackend5', ['AUTH_LDAP_5_SERVER_URI']), - ('awx.sso.backends.RADIUSBackend', ['RADIUS_SERVER']), - ('social_core.backends.google.GoogleOAuth2', ['SOCIAL_AUTH_GOOGLE_OAUTH2_KEY', 'SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET']), - ('social_core.backends.github.GithubOAuth2', ['SOCIAL_AUTH_GITHUB_KEY', 'SOCIAL_AUTH_GITHUB_SECRET']), - ('social_core.backends.open_id_connect.OpenIdConnectAuth', ['SOCIAL_AUTH_OIDC_KEY', 'SOCIAL_AUTH_OIDC_SECRET', 'SOCIAL_AUTH_OIDC_OIDC_ENDPOINT']), - ( - 'social_core.backends.github.GithubOrganizationOAuth2', - ['SOCIAL_AUTH_GITHUB_ORG_KEY', 'SOCIAL_AUTH_GITHUB_ORG_SECRET', 'SOCIAL_AUTH_GITHUB_ORG_NAME'], - ), - ('social_core.backends.github.GithubTeamOAuth2', ['SOCIAL_AUTH_GITHUB_TEAM_KEY', 'SOCIAL_AUTH_GITHUB_TEAM_SECRET', 'SOCIAL_AUTH_GITHUB_TEAM_ID']), - ( - 'social_core.backends.github_enterprise.GithubEnterpriseOAuth2', - [ - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET', - ], - ), - ( - 'social_core.backends.github_enterprise.GithubEnterpriseOrganizationOAuth2', - [ - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_KEY', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_SECRET', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME', - ], - ), - ( - 'social_core.backends.github_enterprise.GithubEnterpriseTeamOAuth2', - [ - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_KEY', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_SECRET', - 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID', - ], - ), - ('social_core.backends.azuread.AzureADOAuth2', ['SOCIAL_AUTH_AZUREAD_OAUTH2_KEY', 'SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET']), - ( - 'awx.sso.backends.SAMLAuth', - [ - 'SOCIAL_AUTH_SAML_SP_ENTITY_ID', - 'SOCIAL_AUTH_SAML_SP_PUBLIC_CERT', - 'SOCIAL_AUTH_SAML_SP_PRIVATE_KEY', - 'SOCIAL_AUTH_SAML_ORG_INFO', - 'SOCIAL_AUTH_SAML_TECHNICAL_CONTACT', - 'SOCIAL_AUTH_SAML_SUPPORT_CONTACT', - 'SOCIAL_AUTH_SAML_ENABLED_IDPS', - ], - ), - ('django.contrib.auth.backends.ModelBackend', []), - ('awx.main.backends.AWXModelBackend', []), - ] - ) - - @classmethod - def get_all_required_settings(cls): - all_required_settings = set(['LICENSE']) - for required_settings in cls.REQUIRED_BACKEND_SETTINGS.values(): - all_required_settings.update(required_settings) - return all_required_settings - - def __init__(self, *args, **kwargs): - kwargs.setdefault('default', self._default_from_required_settings) - super(AuthenticationBackendsField, self).__init__(*args, **kwargs) - - def _default_from_required_settings(self): - from django.conf import settings - - try: - backends = settings._awx_conf_settings._get_default('AUTHENTICATION_BACKENDS') - except AttributeError: - backends = self.REQUIRED_BACKEND_SETTINGS.keys() - # Filter which authentication backends are enabled based on their - # required settings being defined and non-empty. - for backend, required_settings in self.REQUIRED_BACKEND_SETTINGS.items(): - if backend not in backends: - continue - if all([getattr(settings, rs, None) for rs in required_settings]): - continue - backends = [x for x in backends if x != backend] - return backends - - -class LDAPServerURIField(fields.URLField): - def __init__(self, **kwargs): - kwargs.setdefault('schemes', ('ldap', 'ldaps')) - kwargs.setdefault('allow_plain_hostname', True) - super(LDAPServerURIField, self).__init__(**kwargs) - - def run_validators(self, value): - for url in filter(None, re.split(r'[, ]', (value or ''))): - super(LDAPServerURIField, self).run_validators(url) - return value - - -class LDAPConnectionOptionsField(fields.DictField): - default_error_messages = {'invalid_options': _('Invalid connection option(s): {invalid_options}.')} - - def to_representation(self, value): - value = value or {} - opt_names = ldap.OPT_NAMES_DICT - # Convert integer options to their named constants. - repr_value = {} - for opt, opt_value in value.items(): - if opt in opt_names: - repr_value[opt_names[opt]] = opt_value - return repr_value - - def to_internal_value(self, data): - data = super(LDAPConnectionOptionsField, self).to_internal_value(data) - valid_options = dict([(v, k) for k, v in ldap.OPT_NAMES_DICT.items()]) - invalid_options = set(data.keys()) - set(valid_options.keys()) - if invalid_options: - invalid_options = sorted(list(invalid_options)) - options_display = json.dumps(invalid_options).lstrip('[').rstrip(']') - self.fail('invalid_options', invalid_options=options_display) - # Convert named options to their integer constants. - internal_data = {} - for opt_name, opt_value in data.items(): - internal_data[valid_options[opt_name]] = opt_value - return internal_data - - -class LDAPDNField(fields.CharField): - def __init__(self, **kwargs): - super(LDAPDNField, self).__init__(**kwargs) - self.validators.append(validate_ldap_dn) - - def run_validation(self, data=empty): - value = super(LDAPDNField, self).run_validation(data) - # django-auth-ldap expects DN fields (like AUTH_LDAP_REQUIRE_GROUP) - # to be either a valid string or ``None`` (not an empty string) - return None if value == '' else value - - -class LDAPDNListField(fields.StringListField): - def __init__(self, **kwargs): - super(LDAPDNListField, self).__init__(**kwargs) - self.validators.append(lambda dn: list(map(validate_ldap_dn, dn))) - - def run_validation(self, data=empty): - if not isinstance(data, (list, tuple)): - data = [data] - return super(LDAPDNListField, self).run_validation(data) - - -class LDAPDNWithUserField(fields.CharField): - def __init__(self, **kwargs): - super(LDAPDNWithUserField, self).__init__(**kwargs) - self.validators.append(validate_ldap_dn_with_user) - - def run_validation(self, data=empty): - value = super(LDAPDNWithUserField, self).run_validation(data) - # django-auth-ldap expects DN fields (like AUTH_LDAP_USER_DN_TEMPLATE) - # to be either a valid string or ``None`` (not an empty string) - return None if value == '' else value - - -class LDAPFilterField(fields.CharField): - def __init__(self, **kwargs): - super(LDAPFilterField, self).__init__(**kwargs) - self.validators.append(validate_ldap_filter) - - -class LDAPFilterWithUserField(fields.CharField): - def __init__(self, **kwargs): - super(LDAPFilterWithUserField, self).__init__(**kwargs) - self.validators.append(validate_ldap_filter_with_user) - - -class LDAPScopeField(fields.ChoiceField): - def __init__(self, choices=None, **kwargs): - choices = choices or [('SCOPE_BASE', _('Base')), ('SCOPE_ONELEVEL', _('One Level')), ('SCOPE_SUBTREE', _('Subtree'))] - super(LDAPScopeField, self).__init__(choices, **kwargs) - - def to_representation(self, value): - for choice in self.choices.keys(): - if value == getattr(ldap, choice): - return choice - return super(LDAPScopeField, self).to_representation(value) - - def to_internal_value(self, data): - value = super(LDAPScopeField, self).to_internal_value(data) - return getattr(ldap, value) - - -class LDAPSearchField(fields.ListField): - default_error_messages = { - 'invalid_length': _('Expected a list of three items but got {length} instead.'), - 'type_error': _('Expected an instance of LDAPSearch but got {input_type} instead.'), - } - ldap_filter_field_class = LDAPFilterField - - def to_representation(self, value): - if not value: - return [] - if not isinstance(value, LDAPSearch): - self.fail('type_error', input_type=type(value)) - return [ - LDAPDNField().to_representation(value.base_dn), - LDAPScopeField().to_representation(value.scope), - self.ldap_filter_field_class().to_representation(value.filterstr), - ] - - def to_internal_value(self, data): - data = super(LDAPSearchField, self).to_internal_value(data) - if len(data) == 0: - return None - if len(data) != 3: - self.fail('invalid_length', length=len(data)) - return LDAPSearch( - LDAPDNField().run_validation(data[0]), LDAPScopeField().run_validation(data[1]), self.ldap_filter_field_class().run_validation(data[2]) - ) - - -class LDAPSearchWithUserField(LDAPSearchField): - ldap_filter_field_class = LDAPFilterWithUserField - - -class LDAPSearchUnionField(fields.ListField): - default_error_messages = {'type_error': _('Expected an instance of LDAPSearch or LDAPSearchUnion but got {input_type} instead.')} - ldap_search_field_class = LDAPSearchWithUserField - - def to_representation(self, value): - if not value: - return [] - elif isinstance(value, LDAPSearchUnion): - return [self.ldap_search_field_class().to_representation(s) for s in value.searches] - elif isinstance(value, LDAPSearch): - return self.ldap_search_field_class().to_representation(value) - else: - self.fail('type_error', input_type=type(value)) - - def to_internal_value(self, data): - data = super(LDAPSearchUnionField, self).to_internal_value(data) - if len(data) == 0: - return None - if len(data) == 3 and isinstance(data[0], str): - return self.ldap_search_field_class().run_validation(data) - else: - search_args = [] - for i in range(len(data)): - if not isinstance(data[i], list): - raise ValidationError('In order to ultilize LDAP Union, input element No. %d' ' should be a search query array.' % (i + 1)) - try: - search_args.append(self.ldap_search_field_class().run_validation(data[i])) - except Exception as e: - if hasattr(e, 'detail') and isinstance(e.detail, list): - e.detail.insert(0, "Error parsing LDAP Union element No. %d:" % (i + 1)) - raise e - return LDAPSearchUnion(*search_args) - - -class LDAPUserAttrMapField(fields.DictField): - default_error_messages = {'invalid_attrs': _('Invalid user attribute(s): {invalid_attrs}.')} - valid_user_attrs = {'first_name', 'last_name', 'email'} - child = fields.CharField() - - def to_internal_value(self, data): - data = super(LDAPUserAttrMapField, self).to_internal_value(data) - invalid_attrs = set(data.keys()) - self.valid_user_attrs - if invalid_attrs: - invalid_attrs = sorted(list(invalid_attrs)) - attrs_display = json.dumps(invalid_attrs).lstrip('[').rstrip(']') - self.fail('invalid_attrs', invalid_attrs=attrs_display) - return data - - -class LDAPGroupTypeField(fields.ChoiceField, DependsOnMixin): - default_error_messages = { - 'type_error': _('Expected an instance of LDAPGroupType but got {input_type} instead.'), - 'missing_parameters': _('Missing required parameters in {dependency}.'), - 'invalid_parameters': _('Invalid group_type parameters. Expected instance of dict but got {parameters_type} instead.'), - } - - def __init__(self, choices=None, **kwargs): - group_types = get_subclasses(django_auth_ldap.config.LDAPGroupType) - choices = choices or [(x.__name__, x.__name__) for x in group_types] - super(LDAPGroupTypeField, self).__init__(choices, **kwargs) - - def to_representation(self, value): - if not value: - return 'MemberDNGroupType' - if not isinstance(value, django_auth_ldap.config.LDAPGroupType): - self.fail('type_error', input_type=type(value)) - return value.__class__.__name__ - - def to_internal_value(self, data): - data = super(LDAPGroupTypeField, self).to_internal_value(data) - if not data: - return None - - cls = find_class_in_modules(data) - if not cls: - return None - - # Per-group type parameter validation and handling here - - # Backwords compatability. Before AUTH_LDAP_GROUP_TYPE_PARAMS existed - # MemberDNGroupType was the only group type, of the underlying lib, that - # took a parameter. - params = self.get_depends_on() or {} - params_sanitized = dict() - - cls_args = inspect.getfullargspec(cls.__init__).args[1:] - - if cls_args: - if not isinstance(params, dict): - self.fail('invalid_parameters', parameters_type=type(params)) - - for attr in cls_args: - if attr in params: - params_sanitized[attr] = params[attr] - - try: - return cls(**params_sanitized) - except TypeError: - self.fail('missing_parameters', dependency=list(self.depends_on)[0]) - - -class LDAPGroupTypeParamsField(fields.DictField, DependsOnMixin): - default_error_messages = {'invalid_keys': _('Invalid key(s): {invalid_keys}.')} - - def to_internal_value(self, value): - value = super(LDAPGroupTypeParamsField, self).to_internal_value(value) - if not value: - return value - group_type_str = self.get_depends_on() - group_type_str = group_type_str or '' - - group_type_cls = find_class_in_modules(group_type_str) - if not group_type_cls: - # Fail safe - return {} - - invalid_keys = set(value.keys()) - set(inspect.getfullargspec(group_type_cls.__init__).args[1:]) - if invalid_keys: - invalid_keys = sorted(list(invalid_keys)) - keys_display = json.dumps(invalid_keys).lstrip('[').rstrip(']') - self.fail('invalid_keys', invalid_keys=keys_display) - return value - - -class LDAPUserFlagsField(fields.DictField): - default_error_messages = {'invalid_flag': _('Invalid user flag: "{invalid_flag}".')} - valid_user_flags = {'is_superuser', 'is_system_auditor'} - child = LDAPDNListField() - - def to_internal_value(self, data): - data = super(LDAPUserFlagsField, self).to_internal_value(data) - invalid_flags = set(data.keys()) - self.valid_user_flags - if invalid_flags: - self.fail('invalid_flag', invalid_flag=list(invalid_flags)[0]) - return data - - -class LDAPDNMapField(fields.StringListBooleanField): - child = LDAPDNField() - - -class LDAPSingleOrganizationMapField(HybridDictField): - admins = LDAPDNMapField(allow_null=True, required=False) - users = LDAPDNMapField(allow_null=True, required=False) - auditors = LDAPDNMapField(allow_null=True, required=False) - remove_admins = fields.BooleanField(required=False) - remove_users = fields.BooleanField(required=False) - remove_auditors = fields.BooleanField(required=False) - - child = _Forbidden() - - -class LDAPOrganizationMapField(fields.DictField): - child = LDAPSingleOrganizationMapField() - - -class LDAPSingleTeamMapField(HybridDictField): - organization = fields.CharField() - users = LDAPDNMapField(allow_null=True, required=False) - remove = fields.BooleanField(required=False) - - child = _Forbidden() - - -class LDAPTeamMapField(fields.DictField): - child = LDAPSingleTeamMapField() - - -class SocialMapStringRegexField(fields.CharField): - def to_representation(self, value): - if isinstance(value, type(re.compile(''))): - flags = [] - if value.flags & re.I: - flags.append('i') - if value.flags & re.M: - flags.append('m') - return '/{}/{}'.format(value.pattern, ''.join(flags)) - else: - return super(SocialMapStringRegexField, self).to_representation(value) - - def to_internal_value(self, data): - data = super(SocialMapStringRegexField, self).to_internal_value(data) - match = re.match(r'^/(?P.*)/(?P[im]+)?$', data) - if match: - flags = 0 - if match.group('flags'): - if 'i' in match.group('flags'): - flags |= re.I - if 'm' in match.group('flags'): - flags |= re.M - try: - return re.compile(match.group('pattern'), flags) - except re.error as e: - raise ValidationError('{}: {}'.format(e, data)) - return data - - -class SocialMapField(fields.ListField): - default_error_messages = {'type_error': _('Expected None, True, False, a string or list of strings but got {input_type} instead.')} - child = SocialMapStringRegexField() - - def to_representation(self, value): - if isinstance(value, (list, tuple)): - return super(SocialMapField, self).to_representation(value) - elif value in fields.BooleanField.TRUE_VALUES: - return True - elif value in fields.BooleanField.FALSE_VALUES: - return False - elif value in fields.BooleanField.NULL_VALUES: - return None - elif isinstance(value, (str, type(re.compile('')))): - return self.child.to_representation(value) - else: - self.fail('type_error', input_type=type(value)) - - def to_internal_value(self, data): - if isinstance(data, (list, tuple)): - return super(SocialMapField, self).to_internal_value(data) - elif data in fields.BooleanField.TRUE_VALUES: - return True - elif data in fields.BooleanField.FALSE_VALUES: - return False - elif data in fields.BooleanField.NULL_VALUES: - return None - elif isinstance(data, str): - return self.child.run_validation(data) - else: - self.fail('type_error', input_type=type(data)) - - -class SocialSingleOrganizationMapField(HybridDictField): - admins = SocialMapField(allow_null=True, required=False) - users = SocialMapField(allow_null=True, required=False) - remove_admins = fields.BooleanField(required=False) - remove_users = fields.BooleanField(required=False) - organization_alias = SocialMapField(allow_null=True, required=False) - - child = _Forbidden() - - -class SocialOrganizationMapField(fields.DictField): - child = SocialSingleOrganizationMapField() - - -class SocialSingleTeamMapField(HybridDictField): - organization = fields.CharField() - users = SocialMapField(allow_null=True, required=False) - remove = fields.BooleanField(required=False) - - child = _Forbidden() - - -class SocialTeamMapField(fields.DictField): - child = SocialSingleTeamMapField() - - -class SAMLOrgInfoValueField(HybridDictField): - name = fields.CharField() - displayname = fields.CharField() - url = fields.URLField() - - -class SAMLOrgInfoField(fields.DictField): - default_error_messages = {'invalid_lang_code': _('Invalid language code(s) for org info: {invalid_lang_codes}.')} - child = SAMLOrgInfoValueField() - - def to_internal_value(self, data): - data = super(SAMLOrgInfoField, self).to_internal_value(data) - invalid_keys = set() - for key in data.keys(): - if not re.match(r'^[a-z]{2}(?:-[a-z]{2})??$', key, re.I): - invalid_keys.add(key) - if invalid_keys: - invalid_keys = sorted(list(invalid_keys)) - keys_display = json.dumps(invalid_keys).lstrip('[').rstrip(']') - self.fail('invalid_lang_code', invalid_lang_codes=keys_display) - return data - - -class SAMLContactField(HybridDictField): - givenName = fields.CharField() - emailAddress = fields.EmailField() - - -class SAMLIdPField(HybridDictField): - entity_id = fields.CharField() - url = fields.URLField() - x509cert = fields.CharField(validators=[validate_certificate]) - attr_user_permanent_id = fields.CharField(required=False) - attr_first_name = fields.CharField(required=False) - attr_last_name = fields.CharField(required=False) - attr_username = fields.CharField(required=False) - attr_email = fields.CharField(required=False) - - -class SAMLEnabledIdPsField(fields.DictField): - child = SAMLIdPField() - - -class SAMLSecurityField(HybridDictField): - nameIdEncrypted = fields.BooleanField(required=False) - authnRequestsSigned = fields.BooleanField(required=False) - logoutRequestSigned = fields.BooleanField(required=False) - logoutResponseSigned = fields.BooleanField(required=False) - signMetadata = fields.BooleanField(required=False) - wantMessagesSigned = fields.BooleanField(required=False) - wantAssertionsSigned = fields.BooleanField(required=False) - wantAssertionsEncrypted = fields.BooleanField(required=False) - wantNameId = fields.BooleanField(required=False) - wantNameIdEncrypted = fields.BooleanField(required=False) - wantAttributeStatement = fields.BooleanField(required=False) - requestedAuthnContext = fields.StringListBooleanField(required=False) - requestedAuthnContextComparison = fields.CharField(required=False) - metadataValidUntil = fields.CharField(allow_null=True, required=False) - metadataCacheDuration = fields.CharField(allow_null=True, required=False) - signatureAlgorithm = fields.CharField(allow_null=True, required=False) - digestAlgorithm = fields.CharField(allow_null=True, required=False) - - -class SAMLOrgAttrField(HybridDictField): - remove = fields.BooleanField(required=False) - saml_attr = fields.CharField(required=False, allow_null=True) - remove_admins = fields.BooleanField(required=False) - saml_admin_attr = fields.CharField(required=False, allow_null=True) - remove_auditors = fields.BooleanField(required=False) - saml_auditor_attr = fields.CharField(required=False, allow_null=True) - - child = _Forbidden() - - -class SAMLTeamAttrTeamOrgMapField(HybridDictField): - team = fields.CharField(required=True, allow_null=False) - team_alias = fields.CharField(required=False, allow_null=True) - organization = fields.CharField(required=True, allow_null=False) - - child = _Forbidden() - - -class SAMLTeamAttrField(HybridDictField): - team_org_map = fields.ListField(required=False, child=SAMLTeamAttrTeamOrgMapField(), allow_null=True) - remove = fields.BooleanField(required=False) - saml_attr = fields.CharField(required=False, allow_null=True) - - child = _Forbidden() - - -class SAMLUserFlagsAttrField(HybridDictField): - is_superuser_attr = fields.CharField(required=False, allow_null=True) - is_superuser_value = fields.StringListField(required=False, allow_null=True) - is_superuser_role = fields.StringListField(required=False, allow_null=True) - remove_superusers = fields.BooleanField(required=False, allow_null=True) - is_system_auditor_attr = fields.CharField(required=False, allow_null=True) - is_system_auditor_value = fields.StringListField(required=False, allow_null=True) - is_system_auditor_role = fields.StringListField(required=False, allow_null=True) - remove_system_auditors = fields.BooleanField(required=False, allow_null=True) - - child = _Forbidden() diff --git a/awx/sso/ldap_group_types.py b/awx/sso/ldap_group_types.py deleted file mode 100644 index 2a5434c15440..000000000000 --- a/awx/sso/ldap_group_types.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2018 Ansible by Red Hat -# All Rights Reserved. - -# Python -import ldap - -# Django -from django.utils.encoding import force_str - -# 3rd party -from django_auth_ldap.config import LDAPGroupType - - -class PosixUIDGroupType(LDAPGroupType): - def __init__(self, name_attr='cn', ldap_group_user_attr='uid'): - self.ldap_group_user_attr = ldap_group_user_attr - super(PosixUIDGroupType, self).__init__(name_attr) - - """ - An LDAPGroupType subclass that handles non-standard DS. - """ - - def user_groups(self, ldap_user, group_search): - """ - Searches for any group that is either the user's primary or contains the - user as a member. - """ - groups = [] - - try: - user_uid = ldap_user.attrs[self.ldap_group_user_attr][0] - - if 'gidNumber' in ldap_user.attrs: - user_gid = ldap_user.attrs['gidNumber'][0] - filterstr = u'(|(gidNumber=%s)(memberUid=%s))' % ( - self.ldap.filter.escape_filter_chars(user_gid), - self.ldap.filter.escape_filter_chars(user_uid), - ) - else: - filterstr = u'(memberUid=%s)' % (self.ldap.filter.escape_filter_chars(user_uid),) - - search = group_search.search_with_additional_term_string(filterstr) - search.attrlist = [str(self.name_attr)] - groups = search.execute(ldap_user.connection) - except (KeyError, IndexError): - pass - - return groups - - def is_member(self, ldap_user, group_dn): - """ - Returns True if the group is the user's primary group or if the user is - listed in the group's memberUid attribute. - """ - is_member = False - try: - user_uid = ldap_user.attrs[self.ldap_group_user_attr][0] - - try: - is_member = ldap_user.connection.compare_s(force_str(group_dn), 'memberUid', force_str(user_uid)) - except (ldap.UNDEFINED_TYPE, ldap.NO_SUCH_ATTRIBUTE): - is_member = False - - if not is_member: - try: - user_gid = ldap_user.attrs['gidNumber'][0] - is_member = ldap_user.connection.compare_s(force_str(group_dn), 'gidNumber', force_str(user_gid)) - except (ldap.UNDEFINED_TYPE, ldap.NO_SUCH_ATTRIBUTE): - is_member = False - except (KeyError, IndexError): - is_member = False - - return is_member diff --git a/awx/sso/middleware.py b/awx/sso/middleware.py deleted file mode 100644 index f8b2b7974167..000000000000 --- a/awx/sso/middleware.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import urllib.parse - -# Django -from django.conf import settings -from django.utils.functional import LazyObject -from django.shortcuts import redirect - -# Python Social Auth -from social_core.exceptions import SocialAuthBaseException -from social_core.utils import social_logger -from social_django import utils -from social_django.middleware import SocialAuthExceptionMiddleware - - -class SocialAuthMiddleware(SocialAuthExceptionMiddleware): - def process_request(self, request): - if request.path.startswith('/sso'): - # See upgrade blocker note in requirements/README.md - utils.BACKENDS = settings.AUTHENTICATION_BACKENDS - token_key = request.COOKIES.get('token', '') - token_key = urllib.parse.quote(urllib.parse.unquote(token_key).strip('"')) - - if not hasattr(request, 'successful_authenticator'): - request.successful_authenticator = None - - if not request.path.startswith('/sso/') and 'migrations_notran' not in request.path: - if request.user and request.user.is_authenticated: - # The rest of the code base rely hevily on type/inheritance checks, - # LazyObject sent from Django auth middleware can be buggy if not - # converted back to its original object. - if isinstance(request.user, LazyObject) and request.user._wrapped: - request.user = request.user._wrapped - request.session.pop('social_auth_error', None) - request.session.pop('social_auth_last_backend', None) - return self.get_response(request) - - def process_view(self, request, callback, callback_args, callback_kwargs): - if request.path.startswith('/sso/login/'): - request.session['social_auth_last_backend'] = callback_kwargs['backend'] - - def process_exception(self, request, exception): - strategy = getattr(request, 'social_strategy', None) - if strategy is None or self.raise_exception(request, exception): - return - - if isinstance(exception, SocialAuthBaseException) or request.path.startswith('/sso/'): - backend = getattr(request, 'backend', None) - backend_name = getattr(backend, 'name', 'unknown-backend') - - message = self.get_message(request, exception) - if request.session.get('social_auth_last_backend') != backend_name: - backend_name = request.session.get('social_auth_last_backend') - message = request.GET.get('error_description', message) - - full_backend_name = backend_name - try: - idp_name = strategy.request_data()['RelayState'] - full_backend_name = '%s:%s' % (backend_name, idp_name) - except KeyError: - pass - - social_logger.error(message) - - url = self.get_redirect_uri(request, exception) - request.session['social_auth_error'] = (full_backend_name, message) - return redirect(url) - - def get_message(self, request, exception): - msg = str(exception) - if msg and msg[-1] not in '.?!': - msg = msg + '.' - return msg - - def get_redirect_uri(self, request, exception): - strategy = getattr(request, 'social_strategy', None) - return strategy.session_get('next', '') or strategy.setting('LOGIN_ERROR_URL') diff --git a/awx/sso/migrations/0001_initial.py b/awx/sso/migrations/0001_initial.py deleted file mode 100644 index d759e22437b5..000000000000 --- a/awx/sso/migrations/0001_initial.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models -from django.conf import settings - - -class Migration(migrations.Migration): - dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] - - operations = [ - migrations.CreateModel( - name='UserEnterpriseAuth', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('provider', models.CharField(max_length=32, choices=[(b'radius', 'RADIUS'), (b'tacacs+', 'TACACS+')])), - ('user', models.ForeignKey(related_name='enterprise_auth', on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.AlterUniqueTogether(name='userenterpriseauth', unique_together=set([('user', 'provider')])), - ] diff --git a/awx/sso/migrations/0002_expand_provider_options.py b/awx/sso/migrations/0002_expand_provider_options.py deleted file mode 100644 index 68f877717f2f..000000000000 --- a/awx/sso/migrations/0002_expand_provider_options.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [('sso', '0001_initial')] - - operations = [ - migrations.AlterField( - model_name='userenterpriseauth', - name='provider', - field=models.CharField(max_length=32, choices=[('radius', 'RADIUS'), ('tacacs+', 'TACACS+'), ('saml', 'SAML')]), - ) - ] diff --git a/awx/sso/migrations/0003_convert_saml_string_to_list.py b/awx/sso/migrations/0003_convert_saml_string_to_list.py deleted file mode 100644 index bacc25e3c00f..000000000000 --- a/awx/sso/migrations/0003_convert_saml_string_to_list.py +++ /dev/null @@ -1,58 +0,0 @@ -from django.db import migrations, connection -import json - -_values_to_change = ['is_superuser_value', 'is_superuser_role', 'is_system_auditor_value', 'is_system_auditor_role'] - - -def _get_setting(): - with connection.cursor() as cursor: - cursor.execute('SELECT value FROM conf_setting WHERE key= %s', ['SOCIAL_AUTH_SAML_USER_FLAGS_BY_ATTR']) - row = cursor.fetchone() - if row == None: - return {} - existing_setting = row[0] - - try: - existing_json = json.loads(existing_setting) - except json.decoder.JSONDecodeError as e: - print("Failed to decode existing json setting:") - print(existing_setting) - raise e - - return existing_json - - -def _set_setting(value): - with connection.cursor() as cursor: - cursor.execute('UPDATE conf_setting SET value = %s WHERE key = %s', [json.dumps(value), 'SOCIAL_AUTH_SAML_USER_FLAGS_BY_ATTR']) - - -def forwards(app, schema_editor): - # The Operation should use schema_editor to apply any changes it - # wants to make to the database. - existing_json = _get_setting() - for key in _values_to_change: - if existing_json.get(key, None) and isinstance(existing_json.get(key), str): - existing_json[key] = [existing_json.get(key)] - _set_setting(existing_json) - - -def backwards(app, schema_editor): - existing_json = _get_setting() - for key in _values_to_change: - if existing_json.get(key, None) and not isinstance(existing_json.get(key), str): - try: - existing_json[key] = existing_json.get(key).pop() - except IndexError: - existing_json[key] = "" - _set_setting(existing_json) - - -class Migration(migrations.Migration): - dependencies = [ - ('sso', '0002_expand_provider_options'), - ] - - operations = [ - migrations.RunPython(forwards, backwards), - ] diff --git a/awx/sso/models.py b/awx/sso/models.py deleted file mode 100644 index 28eb23857f4b..000000000000 --- a/awx/sso/models.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Django -from django.db import models -from django.contrib.auth.models import User -from django.utils.translation import gettext_lazy as _ - - -class UserEnterpriseAuth(models.Model): - """Enterprise Auth association model""" - - PROVIDER_CHOICES = (('radius', _('RADIUS')), ('tacacs+', _('TACACS+')), ('saml', _('SAML'))) - - class Meta: - unique_together = ('user', 'provider') - - user = models.ForeignKey(User, related_name='enterprise_auth', on_delete=models.CASCADE) - provider = models.CharField(max_length=32, choices=PROVIDER_CHOICES) diff --git a/awx/sso/saml_pipeline.py b/awx/sso/saml_pipeline.py deleted file mode 100644 index e8c4f9ff6d8c..000000000000 --- a/awx/sso/saml_pipeline.py +++ /dev/null @@ -1,291 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import re -import logging - -# Django -from django.conf import settings - -from awx.main.models import Team -from awx.sso.common import create_org_and_teams, reconcile_users_org_team_mappings, get_orgs_by_ids - -logger = logging.getLogger('awx.sso.saml_pipeline') - - -def populate_user(backend, details, user=None, *args, **kwargs): - if not user: - return - - # Build the in-memory settings for how this user should be modeled - desired_org_state = {} - desired_team_state = {} - orgs_to_create = [] - teams_to_create = {} - _update_user_orgs_by_saml_attr(backend, desired_org_state, orgs_to_create, **kwargs) - _update_user_teams_by_saml_attr(desired_team_state, teams_to_create, **kwargs) - _update_user_orgs(backend, desired_org_state, orgs_to_create, user) - _update_user_teams(backend, desired_team_state, teams_to_create, user) - - # If the SAML adapter is allowed to create objects, lets do that first - create_org_and_teams(orgs_to_create, teams_to_create, 'SAML', settings.SAML_AUTO_CREATE_OBJECTS) - - # Finally reconcile the user - reconcile_users_org_team_mappings(user, desired_org_state, desired_team_state, 'SAML') - - -def _update_m2m_from_expression(user, expr, remove=True): - """ - Helper function to update m2m relationship based on user matching one or - more expressions. - """ - should_add = False - if expr is None or not expr: - pass - elif expr is True: - should_add = True - else: - if isinstance(expr, (str, type(re.compile('')))): - expr = [expr] - for ex in expr: - if isinstance(ex, str): - if user.username == ex or user.email == ex: - should_add = True - elif isinstance(ex, type(re.compile(''))): - if ex.match(user.username) or ex.match(user.email): - should_add = True - if should_add: - return True - elif remove: - return False - else: - return None - - -def _update_user_orgs(backend, desired_org_state, orgs_to_create, user=None): - """ - Update organization memberships for the given user based on mapping rules - defined in settings. - """ - org_map = backend.setting('ORGANIZATION_MAP') or {} - for org_name, org_opts in org_map.items(): - organization_alias = org_opts.get('organization_alias') - if organization_alias: - organization_name = organization_alias - else: - organization_name = org_name - if organization_name not in orgs_to_create: - orgs_to_create.append(organization_name) - - remove = bool(org_opts.get('remove', True)) - - if organization_name not in desired_org_state: - desired_org_state[organization_name] = {} - - for role_name, user_type in (('admin_role', 'admins'), ('member_role', 'users'), ('auditor_role', 'auditors')): - is_member_expression = org_opts.get(user_type, None) - remove_members = bool(org_opts.get('remove_{}'.format(user_type), remove)) - has_role = _update_m2m_from_expression(user, is_member_expression, remove_members) - desired_org_state[organization_name][role_name] = has_role - - -def _update_user_teams(backend, desired_team_state, teams_to_create, user=None): - """ - Update team memberships for the given user based on mapping rules defined - in settings. - """ - - team_map = backend.setting('TEAM_MAP') or {} - for team_name, team_opts in team_map.items(): - # Get or create the org to update. - if 'organization' not in team_opts: - continue - teams_to_create[team_name] = team_opts['organization'] - users_expr = team_opts.get('users', None) - remove = bool(team_opts.get('remove', True)) - add_or_remove = _update_m2m_from_expression(user, users_expr, remove) - if add_or_remove is not None: - org_name = team_opts['organization'] - if org_name not in desired_team_state: - desired_team_state[org_name] = {} - desired_team_state[org_name][team_name] = {'member_role': add_or_remove} - - -def _update_user_orgs_by_saml_attr(backend, desired_org_state, orgs_to_create, **kwargs): - org_map = settings.SOCIAL_AUTH_SAML_ORGANIZATION_ATTR - roles_and_flags = ( - ('member_role', 'remove', 'saml_attr'), - ('admin_role', 'remove_admins', 'saml_admin_attr'), - ('auditor_role', 'remove_auditors', 'saml_auditor_attr'), - ) - - # If the remove_flag was present we need to load all of the orgs and remove the user from the role - all_orgs = None - for role, remove_flag, _ in roles_and_flags: - remove = bool(org_map.get(remove_flag, True)) - if remove: - # Only get the all orgs once, and only if needed - if all_orgs is None: - all_orgs = get_orgs_by_ids() - for org_name in all_orgs.keys(): - if org_name not in desired_org_state: - desired_org_state[org_name] = {} - desired_org_state[org_name][role] = False - - # Now we can add the user as a member/admin/auditor for any orgs they have specified - for role, _, attr_flag in roles_and_flags: - if org_map.get(attr_flag) is None: - continue - saml_attr_values = kwargs.get('response', {}).get('attributes', {}).get(org_map.get(attr_flag), []) - for org_name in saml_attr_values: - try: - organization_alias = backend.setting('ORGANIZATION_MAP').get(org_name).get('organization_alias') - if organization_alias is not None: - organization_name = organization_alias - else: - organization_name = org_name - except Exception: - organization_name = org_name - if organization_name not in orgs_to_create: - orgs_to_create.append(organization_name) - if organization_name not in desired_org_state: - desired_org_state[organization_name] = {} - desired_org_state[organization_name][role] = True - - -def _update_user_teams_by_saml_attr(desired_team_state, teams_to_create, **kwargs): - # - # Map users into organizations based on SOCIAL_AUTH_SAML_TEAM_ATTR setting - # - team_map = settings.SOCIAL_AUTH_SAML_TEAM_ATTR - if team_map.get('saml_attr') is None: - return - - all_teams = None - # The role and flag is hard coded here but intended to be flexible in case we ever wanted to add another team type - for role, remove_flag in [('member_role', 'remove')]: - remove = bool(team_map.get(remove_flag, True)) - if remove: - # Only get the all orgs once, and only if needed - if all_teams is None: - all_teams = Team.objects.all().values_list('name', 'organization__name') - for team_name, organization_name in all_teams: - if organization_name not in desired_team_state: - desired_team_state[organization_name] = {} - desired_team_state[organization_name][team_name] = {role: False} - - saml_team_names = set(kwargs.get('response', {}).get('attributes', {}).get(team_map['saml_attr'], [])) - - for team_name_map in team_map.get('team_org_map', []): - team_name = team_name_map.get('team', None) - team_alias = team_name_map.get('team_alias', None) - organization_name = team_name_map.get('organization', None) - if team_name in saml_team_names: - if not organization_name: - # Settings field validation should prevent this. - logger.error("organization name invalid for team {}".format(team_name)) - continue - - if team_alias: - team_name = team_alias - - teams_to_create[team_name] = organization_name - user_is_member_of_team = True - else: - user_is_member_of_team = False - - if organization_name not in desired_team_state: - desired_team_state[organization_name] = {} - desired_team_state[organization_name][team_name] = {'member_role': user_is_member_of_team} - - -def _get_matches(list1, list2): - # Because we are just doing an intersection here we don't really care which list is in which parameter - - # A SAML provider could return either a string or a list of items so we need to coerce the SAML value into a list (if needed) - if not isinstance(list1, (list, tuple)): - list1 = [list1] - - # In addition, we used to allow strings in the SAML config instead of Lists. The migration should take case of that but just in case, we will convert our list too - if not isinstance(list2, (list, tuple)): - list2 = [list2] - - return set(list1).intersection(set(list2)) - - -def _check_flag(user, flag, attributes, user_flags_settings): - ''' - Helper function to set the is_superuser is_system_auditor flags for the SAML adapter - Returns the new flag and whether or not it changed the flag - ''' - new_flag = False - is_role_key = "is_%s_role" % (flag) - is_attr_key = "is_%s_attr" % (flag) - is_value_key = "is_%s_value" % (flag) - remove_setting = "remove_%ss" % (flag) - - # Check to see if we are respecting a role and, if so, does our user have that role? - required_roles = user_flags_settings.get(is_role_key, None) - if required_roles: - matching_roles = _get_matches(required_roles, attributes.get('Role', [])) - - # We do a 2 layer check here so that we don't spit out the else message if there is no role defined - if matching_roles: - logger.debug("User %s has %s role(s) %s" % (user.username, flag, ', '.join(matching_roles))) - new_flag = True - else: - logger.debug("User %s is missing the %s role(s) %s" % (user.username, flag, ', '.join(required_roles))) - - # Next, check to see if we are respecting an attribute; this will take priority over the role if its defined - attr_setting = user_flags_settings.get(is_attr_key, None) - if attr_setting and attributes.get(attr_setting, None): - # Do we have a required value for the attribute - required_value = user_flags_settings.get(is_value_key, None) - if required_value: - # If so, check and see if the value of the attr matches the required value - saml_user_attribute_value = attributes.get(attr_setting, None) - matching_values = _get_matches(required_value, saml_user_attribute_value) - - if matching_values: - logger.debug("Giving %s %s from attribute %s with matching values %s" % (user.username, flag, attr_setting, ', '.join(matching_values))) - new_flag = True - # if they don't match make sure that new_flag is false - else: - logger.debug( - "Refusing %s for %s because attr %s (%s) did not match value(s) %s" - % (flag, user.username, attr_setting, ", ".join(saml_user_attribute_value), ', '.join(required_value)) - ) - new_flag = False - # If there was no required value then we can just allow them in because of the attribute - else: - logger.debug("Giving %s %s from attribute %s" % (user.username, flag, attr_setting)) - new_flag = True - - # Get the users old flag - old_value = getattr(user, "is_%s" % (flag)) - - # If we are not removing the flag and they were a system admin and now we don't want them to be just return - remove_flag = user_flags_settings.get(remove_setting, True) - if not remove_flag and (old_value and not new_flag): - logger.debug("Remove flag %s preventing removal of %s for %s" % (remove_flag, flag, user.username)) - return old_value, False - - # If the user was flagged and we are going to make them not flagged make sure there is a message - if old_value and not new_flag: - logger.debug("Revoking %s from %s" % (flag, user.username)) - - return new_flag, old_value != new_flag - - -def update_user_flags(backend, details, user=None, *args, **kwargs): - user_flags_settings = settings.SOCIAL_AUTH_SAML_USER_FLAGS_BY_ATTR - - attributes = kwargs.get('response', {}).get('attributes', {}) - logger.debug("User attributes for %s: %s" % (user.username, attributes)) - - user.is_superuser, superuser_changed = _check_flag(user, 'superuser', attributes, user_flags_settings) - user.is_system_auditor, auditor_changed = _check_flag(user, 'system_auditor', attributes, user_flags_settings) - - if superuser_changed or auditor_changed: - user.save() diff --git a/awx/sso/social_base_pipeline.py b/awx/sso/social_base_pipeline.py deleted file mode 100644 index ccdaf1d20079..000000000000 --- a/awx/sso/social_base_pipeline.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python Social Auth -from social_core.exceptions import AuthException - -# Django -from django.utils.translation import gettext_lazy as _ - - -class AuthNotFound(AuthException): - def __init__(self, backend, email_or_uid, *args, **kwargs): - self.email_or_uid = email_or_uid - super(AuthNotFound, self).__init__(backend, *args, **kwargs) - - def __str__(self): - return _('An account cannot be found for {0}').format(self.email_or_uid) - - -class AuthInactive(AuthException): - def __str__(self): - return _('Your account is inactive') - - -def check_user_found_or_created(backend, details, user=None, *args, **kwargs): - if not user: - email_or_uid = details.get('email') or kwargs.get('email') or kwargs.get('uid') or '???' - raise AuthNotFound(backend, email_or_uid) - - -def set_is_active_for_new_user(strategy, details, user=None, *args, **kwargs): - if kwargs.get('is_new', False): - details['is_active'] = True - return {'details': details} - - -def prevent_inactive_login(backend, details, user=None, *args, **kwargs): - if user and not user.is_active: - raise AuthInactive(backend) diff --git a/awx/sso/social_pipeline.py b/awx/sso/social_pipeline.py deleted file mode 100644 index b4fb4c1fe323..000000000000 --- a/awx/sso/social_pipeline.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import re -import logging - -from awx.sso.common import get_or_create_org_with_default_galaxy_cred - -logger = logging.getLogger('awx.sso.social_pipeline') - - -def _update_m2m_from_expression(user, related, expr, remove=True): - """ - Helper function to update m2m relationship based on user matching one or - more expressions. - """ - should_add = False - if expr is None: - return - elif not expr: - pass - elif expr is True: - should_add = True - else: - if isinstance(expr, (str, type(re.compile('')))): - expr = [expr] - for ex in expr: - if isinstance(ex, str): - if user.username == ex or user.email == ex: - should_add = True - elif isinstance(ex, type(re.compile(''))): - if ex.match(user.username) or ex.match(user.email): - should_add = True - if should_add: - related.add(user) - elif remove: - related.remove(user) - - -def update_user_orgs(backend, details, user=None, *args, **kwargs): - """ - Update organization memberships for the given user based on mapping rules - defined in settings. - """ - if not user: - return - - org_map = backend.setting('ORGANIZATION_MAP') or {} - for org_name, org_opts in org_map.items(): - organization_alias = org_opts.get('organization_alias') - if organization_alias: - organization_name = organization_alias - else: - organization_name = org_name - org = get_or_create_org_with_default_galaxy_cred(name=organization_name) - - # Update org admins from expression(s). - remove = bool(org_opts.get('remove', True)) - admins_expr = org_opts.get('admins', None) - remove_admins = bool(org_opts.get('remove_admins', remove)) - _update_m2m_from_expression(user, org.admin_role.members, admins_expr, remove_admins) - - # Update org users from expression(s). - users_expr = org_opts.get('users', None) - remove_users = bool(org_opts.get('remove_users', remove)) - _update_m2m_from_expression(user, org.member_role.members, users_expr, remove_users) - - -def update_user_teams(backend, details, user=None, *args, **kwargs): - """ - Update team memberships for the given user based on mapping rules defined - in settings. - """ - if not user: - return - from awx.main.models import Team - - team_map = backend.setting('TEAM_MAP') or {} - for team_name, team_opts in team_map.items(): - # Get or create the org to update. - if 'organization' not in team_opts: - continue - org = get_or_create_org_with_default_galaxy_cred(name=team_opts['organization']) - - # Update team members from expression(s). - team = Team.objects.get_or_create(name=team_name, organization=org)[0] - users_expr = team_opts.get('users', None) - remove = bool(team_opts.get('remove', True)) - _update_m2m_from_expression(user, team.member_role.members, users_expr, remove) diff --git a/awx/sso/tests/conftest.py b/awx/sso/tests/conftest.py deleted file mode 100644 index f94b1c528f6d..000000000000 --- a/awx/sso/tests/conftest.py +++ /dev/null @@ -1,34 +0,0 @@ -import pytest - -from django.contrib.auth.models import User - -from awx.sso.backends import TACACSPlusBackend -from awx.sso.models import UserEnterpriseAuth - - -@pytest.fixture -def tacacsplus_backend(): - return TACACSPlusBackend() - - -@pytest.fixture -def existing_normal_user(): - try: - user = User.objects.get(username="alice") - except User.DoesNotExist: - user = User(username="alice", password="password") - user.save() - return user - - -@pytest.fixture -def existing_tacacsplus_user(): - try: - user = User.objects.get(username="foo") - except User.DoesNotExist: - user = User(username="foo") - user.set_unusable_password() - user.save() - enterprise_auth = UserEnterpriseAuth(user=user, provider='tacacs+') - enterprise_auth.save() - return user diff --git a/awx/sso/tests/functional/__init__.py b/awx/sso/tests/functional/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/awx/sso/tests/functional/test_backends.py b/awx/sso/tests/functional/test_backends.py deleted file mode 100644 index a0d2c31da3e2..000000000000 --- a/awx/sso/tests/functional/test_backends.py +++ /dev/null @@ -1,115 +0,0 @@ -import pytest -from awx.sso.backends import _update_m2m_from_groups - - -class MockLDAPGroups(object): - def is_member_of(self, group_dn): - return bool(group_dn) - - -class MockLDAPUser(object): - def _get_groups(self): - return MockLDAPGroups() - - -@pytest.mark.parametrize( - "setting, expected_result", - [ - (True, True), - ('something', True), - (False, False), - ('', False), - ], -) -def test_mock_objects(setting, expected_result): - ldap_user = MockLDAPUser() - assert ldap_user._get_groups().is_member_of(setting) == expected_result - - -@pytest.mark.parametrize( - "opts, remove, expected_result", - [ - # In these case we will pass no opts so we should get None as a return in all cases - ( - None, - False, - None, - ), - ( - None, - True, - None, - ), - # Next lets test with empty opts ([]) This should return False if remove is True and None otherwise - ( - [], - True, - False, - ), - ( - [], - False, - None, - ), - # Next opts is True, this will always return True - ( - True, - True, - True, - ), - ( - True, - False, - True, - ), - # If we get only a non-string as an option we hit a continue and will either return None or False depending on the remove flag - ( - [32], - False, - None, - ), - ( - [32], - True, - False, - ), - # Finally we need to test whether or not a user should be allowed in or not. - # We use a mock class for ldap_user that simply returns true/false based on the otps - ( - ['true'], - False, - True, - ), - # In this test we are going to pass a string to test the part of the code that coverts strings into array, this should give us True - ( - 'something', - True, - True, - ), - ( - [''], - False, - None, - ), - ( - False, - True, - False, - ), - # Empty strings are considered opts == None and will result in None or False based on the remove flag - ( - '', - True, - False, - ), - ( - '', - False, - None, - ), - ], -) -@pytest.mark.django_db -def test__update_m2m_from_groups(opts, remove, expected_result): - ldap_user = MockLDAPUser() - assert expected_result == _update_m2m_from_groups(ldap_user, opts, remove) diff --git a/awx/sso/tests/functional/test_common.py b/awx/sso/tests/functional/test_common.py deleted file mode 100644 index 4fc3edd841bb..000000000000 --- a/awx/sso/tests/functional/test_common.py +++ /dev/null @@ -1,280 +0,0 @@ -import pytest -from collections import Counter -from django.core.exceptions import FieldError -from django.utils.timezone import now - -from awx.main.models import Credential, CredentialType, Organization, Team, User -from awx.sso.common import get_orgs_by_ids, reconcile_users_org_team_mappings, create_org_and_teams, get_or_create_org_with_default_galaxy_cred - - -@pytest.mark.django_db -class TestCommonFunctions: - @pytest.fixture - def orgs(self): - o1 = Organization.objects.create(name='Default1') - o2 = Organization.objects.create(name='Default2') - o3 = Organization.objects.create(name='Default3') - return (o1, o2, o3) - - @pytest.fixture - def galaxy_credential(self): - galaxy_type = CredentialType.objects.create(kind='galaxy') - cred = Credential( - created=now(), modified=now(), name='Ansible Galaxy', managed=True, credential_type=galaxy_type, inputs={'url': 'https://galaxy.ansible.com/'} - ) - cred.save() - - def test_get_orgs_by_ids(self, orgs): - orgs_and_ids = get_orgs_by_ids() - o1, o2, o3 = orgs - assert Counter(orgs_and_ids.keys()) == Counter([o1.name, o2.name, o3.name]) - assert Counter(orgs_and_ids.values()) == Counter([o1.id, o2.id, o3.id]) - - def test_reconcile_users_org_team_mappings(self): - # Create objects for us to play with - user = User.objects.create(username='user1@foo.com', last_name='foo', first_name='bar', email='user1@foo.com', is_active=True) - org1 = Organization.objects.create(name='Default1') - org2 = Organization.objects.create(name='Default2') - team1 = Team.objects.create(name='Team1', organization=org1) - team2 = Team.objects.create(name='Team1', organization=org2) - - # Try adding nothing - reconcile_users_org_team_mappings(user, {}, {}, 'Nada') - assert list(user.roles.all()) == [] - - # Add a user to an org that does not exist (should have no affect) - reconcile_users_org_team_mappings( - user, - { - 'junk': {'member_role': True}, - }, - {}, - 'Nada', - ) - assert list(user.roles.all()) == [] - - # Remove a user to an org that does not exist (should have no affect) - reconcile_users_org_team_mappings( - user, - { - 'junk': {'member_role': False}, - }, - {}, - 'Nada', - ) - assert list(user.roles.all()) == [] - - # Add the user to the orgs - reconcile_users_org_team_mappings(user, {org1.name: {'member_role': True}, org2.name: {'member_role': True}}, {}, 'Nada') - assert len(user.roles.all()) == 2 - assert user in org1.member_role - assert user in org2.member_role - - # Remove the user from the orgs - reconcile_users_org_team_mappings(user, {org1.name: {'member_role': False}, org2.name: {'member_role': False}}, {}, 'Nada') - assert list(user.roles.all()) == [] - assert user not in org1.member_role - assert user not in org2.member_role - - # Remove the user from the orgs (again, should have no affect) - reconcile_users_org_team_mappings(user, {org1.name: {'member_role': False}, org2.name: {'member_role': False}}, {}, 'Nada') - assert list(user.roles.all()) == [] - assert user not in org1.member_role - assert user not in org2.member_role - - # Add a user back to the member role - reconcile_users_org_team_mappings( - user, - { - org1.name: { - 'member_role': True, - }, - }, - {}, - 'Nada', - ) - users_roles = set(user.roles.values_list('pk', flat=True)) - assert len(users_roles) == 1 - assert user in org1.member_role - - # Add the user to additional roles - reconcile_users_org_team_mappings( - user, - { - org1.name: {'admin_role': True, 'auditor_role': True}, - }, - {}, - 'Nada', - ) - assert len(user.roles.all()) == 3 - assert user in org1.member_role - assert user in org1.admin_role - assert user in org1.auditor_role - - # Add a user to a non-existent role (results in FieldError exception) - with pytest.raises(FieldError): - reconcile_users_org_team_mappings( - user, - { - org1.name: { - 'dne_role': True, - }, - }, - {}, - 'Nada', - ) - - # Try adding a user to a role that should not exist on an org (technically this works at this time) - reconcile_users_org_team_mappings( - user, - { - org1.name: { - 'read_role_id': True, - }, - }, - {}, - 'Nada', - ) - assert len(user.roles.all()) == 4 - assert user in org1.member_role - assert user in org1.admin_role - assert user in org1.auditor_role - - # Remove all of the org perms to test team perms - reconcile_users_org_team_mappings( - user, - { - org1.name: { - 'read_role_id': False, - 'member_role': False, - 'admin_role': False, - 'auditor_role': False, - }, - }, - {}, - 'Nada', - ) - assert list(user.roles.all()) == [] - - # Add the user as a member to one of the teams - reconcile_users_org_team_mappings(user, {}, {org1.name: {team1.name: {'member_role': True}}}, 'Nada') - assert len(user.roles.all()) == 1 - assert user in team1.member_role - # Validate that the user did not become a member of a team with the same name in a different org - assert user not in team2.member_role - - # Remove the user from the team - reconcile_users_org_team_mappings(user, {}, {org1.name: {team1.name: {'member_role': False}}}, 'Nada') - assert list(user.roles.all()) == [] - assert user not in team1.member_role - - # Remove the user from the team again - reconcile_users_org_team_mappings(user, {}, {org1.name: {team1.name: {'member_role': False}}}, 'Nada') - assert list(user.roles.all()) == [] - - # Add the user to a team that does not exist (should have no affect) - reconcile_users_org_team_mappings(user, {}, {org1.name: {'junk': {'member_role': True}}}, 'Nada') - assert list(user.roles.all()) == [] - - # Remove the user from a team that does not exist (should have no affect) - reconcile_users_org_team_mappings(user, {}, {org1.name: {'junk': {'member_role': False}}}, 'Nada') - assert list(user.roles.all()) == [] - - # Test a None setting - reconcile_users_org_team_mappings(user, {}, {org1.name: {'junk': {'member_role': None}}}, 'Nada') - assert list(user.roles.all()) == [] - - # Add the user multiple teams in different orgs - reconcile_users_org_team_mappings(user, {}, {org1.name: {team1.name: {'member_role': True}}, org2.name: {team2.name: {'member_role': True}}}, 'Nada') - assert len(user.roles.all()) == 2 - assert user in team1.member_role - assert user in team2.member_role - - # Remove the user from just one of the teams - reconcile_users_org_team_mappings(user, {}, {org2.name: {team2.name: {'member_role': False}}}, 'Nada') - assert len(user.roles.all()) == 1 - assert user in team1.member_role - assert user not in team2.member_role - - @pytest.mark.parametrize( - "org_list, team_map, can_create, org_count, team_count", - [ - # In this case we will only pass in organizations - ( - ["org1", "org2"], - {}, - True, - 2, - 0, - ), - # In this case we will only pass in teams but the orgs will be created from the teams - ( - [], - {"team1": "org1", "team2": "org2"}, - True, - 2, - 2, - ), - # In this case we will reuse an org - ( - ["org1"], - {"team1": "org1", "team2": "org1"}, - True, - 1, - 2, - ), - # In this case we have a combination of orgs, orgs reused and an org created by a team - ( - ["org1", "org2", "org3"], - {"team1": "org1", "team2": "org4"}, - True, - 4, - 2, - ), - # In this case we will test a case that the UI should prevent and have a team with no Org - # This should create org1/2 but only team1 - ( - ["org1"], - {"team1": "org2", "team2": None}, - True, - 2, - 1, - ), - # Block any creation with the can_create flag - ( - ["org1"], - {"team1": "org2", "team2": None}, - False, - 0, - 0, - ), - ], - ) - def test_create_org_and_teams(self, galaxy_credential, org_list, team_map, can_create, org_count, team_count): - create_org_and_teams(org_list, team_map, 'py.test', can_create=can_create) - assert Organization.objects.count() == org_count - assert Team.objects.count() == team_count - - def test_get_or_create_org_with_default_galaxy_cred_add_galaxy_cred(self, galaxy_credential): - # If this method creates the org it should get the default galaxy credential - num_orgs = 4 - for number in range(1, (num_orgs + 1)): - get_or_create_org_with_default_galaxy_cred(name=f"Default {number}") - - assert Organization.objects.count() == 4 - - for o in Organization.objects.all(): - assert o.galaxy_credentials.count() == 1 - assert o.galaxy_credentials.first().name == 'Ansible Galaxy' - - def test_get_or_create_org_with_default_galaxy_cred_no_galaxy_cred(self, galaxy_credential): - # If the org is pre-created, we should not add the galaxy_credential - num_orgs = 4 - for number in range(1, (num_orgs + 1)): - Organization.objects.create(name=f"Default {number}") - get_or_create_org_with_default_galaxy_cred(name=f"Default {number}") - - assert Organization.objects.count() == 4 - - for o in Organization.objects.all(): - assert o.galaxy_credentials.count() == 0 diff --git a/awx/sso/tests/functional/test_get_or_set_enterprise_user.py b/awx/sso/tests/functional/test_get_or_set_enterprise_user.py deleted file mode 100644 index 3f37b41df319..000000000000 --- a/awx/sso/tests/functional/test_get_or_set_enterprise_user.py +++ /dev/null @@ -1,37 +0,0 @@ -# Python -import pytest -from unittest import mock - -# AWX -from awx.sso.backends import _get_or_set_enterprise_user - - -@pytest.mark.django_db -def test_fetch_user_if_exist(existing_tacacsplus_user): - with mock.patch('awx.sso.backends.logger') as mocked_logger: - new_user = _get_or_set_enterprise_user("foo", "password", "tacacs+") - mocked_logger.debug.assert_not_called() - mocked_logger.warning.assert_not_called() - assert new_user == existing_tacacsplus_user - - -@pytest.mark.django_db -def test_create_user_if_not_exist(existing_tacacsplus_user): - with mock.patch('awx.sso.backends.logger') as mocked_logger: - new_user = _get_or_set_enterprise_user("bar", "password", "tacacs+") - mocked_logger.debug.assert_called_once_with(u'Created enterprise user bar via TACACS+ backend.') - assert new_user != existing_tacacsplus_user - - -@pytest.mark.django_db -def test_created_user_has_no_usable_password(): - new_user = _get_or_set_enterprise_user("bar", "password", "tacacs+") - assert not new_user.has_usable_password() - - -@pytest.mark.django_db -def test_non_enterprise_user_does_not_get_pass(existing_normal_user): - with mock.patch('awx.sso.backends.logger') as mocked_logger: - new_user = _get_or_set_enterprise_user("alice", "password", "tacacs+") - mocked_logger.warning.assert_called_once_with(u'Enterprise user alice already defined in Tower.') - assert new_user is None diff --git a/awx/sso/tests/functional/test_ldap.py b/awx/sso/tests/functional/test_ldap.py deleted file mode 100644 index 881ab29e2b4f..000000000000 --- a/awx/sso/tests/functional/test_ldap.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.test.utils import override_settings -import ldap -import pytest - -from awx.sso.backends import LDAPSettings - - -@override_settings(AUTH_LDAP_CONNECTION_OPTIONS={ldap.OPT_NETWORK_TIMEOUT: 60}) -@pytest.mark.django_db -def test_ldap_with_custom_timeout(): - settings = LDAPSettings() - assert settings.CONNECTION_OPTIONS == {ldap.OPT_NETWORK_TIMEOUT: 60} - - -@override_settings(AUTH_LDAP_CONNECTION_OPTIONS={ldap.OPT_REFERRALS: 0}) -@pytest.mark.django_db -def test_ldap_with_missing_timeout(): - settings = LDAPSettings() - assert settings.CONNECTION_OPTIONS == {ldap.OPT_REFERRALS: 0, ldap.OPT_NETWORK_TIMEOUT: 30} diff --git a/awx/sso/tests/functional/test_saml_pipeline.py b/awx/sso/tests/functional/test_saml_pipeline.py deleted file mode 100644 index 628d793d4eec..000000000000 --- a/awx/sso/tests/functional/test_saml_pipeline.py +++ /dev/null @@ -1,639 +0,0 @@ -import pytest -import re - -from django.test.utils import override_settings -from awx.main.models import User, Organization, Team -from awx.sso.saml_pipeline import ( - _update_m2m_from_expression, - _update_user_orgs, - _update_user_teams, - _update_user_orgs_by_saml_attr, - _update_user_teams_by_saml_attr, - _check_flag, -) - -# from unittest import mock -# from django.utils.timezone import now -# , Credential, CredentialType - - -@pytest.fixture -def users(): - u1 = User.objects.create(username='user1@foo.com', last_name='foo', first_name='bar', email='user1@foo.com') - u2 = User.objects.create(username='user2@foo.com', last_name='foo', first_name='bar', email='user2@foo.com') - u3 = User.objects.create(username='user3@foo.com', last_name='foo', first_name='bar', email='user3@foo.com') - return (u1, u2, u3) - - -@pytest.mark.django_db -class TestSAMLPopulateUser: - # The main populate_user does not need to be tested since its just a conglomeration of other functions that we test - # This test is here in case someone alters the code in the future in a way that does require testing - def test_populate_user(self): - assert True - - -@pytest.mark.django_db -class TestSAMLSimpleMaps: - # This tests __update_user_orgs and __update_user_teams - @pytest.fixture - def backend(self): - class Backend: - s = { - 'ORGANIZATION_MAP': { - 'Default': { - 'remove': True, - 'admins': 'foobar', - 'remove_admins': True, - 'users': 'foo', - 'remove_users': True, - 'organization_alias': '', - } - }, - 'TEAM_MAP': {'Blue': {'organization': 'Default', 'remove': True, 'users': ''}, 'Red': {'organization': 'Default', 'remove': True, 'users': ''}}, - } - - def setting(self, key): - return self.s[key] - - return Backend() - - def test__update_user_orgs(self, backend, users): - u1, u2, u3 = users - - # Test user membership logic with regular expressions - backend.setting('ORGANIZATION_MAP')['Default']['admins'] = re.compile('.*') - backend.setting('ORGANIZATION_MAP')['Default']['users'] = re.compile('.*') - - desired_org_state = {} - orgs_to_create = [] - _update_user_orgs(backend, desired_org_state, orgs_to_create, u1) - _update_user_orgs(backend, desired_org_state, orgs_to_create, u2) - _update_user_orgs(backend, desired_org_state, orgs_to_create, u3) - - assert desired_org_state == {'Default': {'member_role': True, 'admin_role': True, 'auditor_role': False}} - assert orgs_to_create == ['Default'] - - # Test remove feature enabled - backend.setting('ORGANIZATION_MAP')['Default']['admins'] = '' - backend.setting('ORGANIZATION_MAP')['Default']['users'] = '' - backend.setting('ORGANIZATION_MAP')['Default']['remove_admins'] = True - backend.setting('ORGANIZATION_MAP')['Default']['remove_users'] = True - desired_org_state = {} - orgs_to_create = [] - _update_user_orgs(backend, desired_org_state, orgs_to_create, u1) - assert desired_org_state == {'Default': {'member_role': False, 'admin_role': False, 'auditor_role': False}} - assert orgs_to_create == ['Default'] - - # Test remove feature disabled - backend.setting('ORGANIZATION_MAP')['Default']['remove_admins'] = False - backend.setting('ORGANIZATION_MAP')['Default']['remove_users'] = False - desired_org_state = {} - orgs_to_create = [] - _update_user_orgs(backend, desired_org_state, orgs_to_create, u2) - - assert desired_org_state == {'Default': {'member_role': None, 'admin_role': None, 'auditor_role': False}} - assert orgs_to_create == ['Default'] - - # Test organization alias feature - backend.setting('ORGANIZATION_MAP')['Default']['organization_alias'] = 'Default_Alias' - orgs_to_create = [] - _update_user_orgs(backend, {}, orgs_to_create, u1) - assert orgs_to_create == ['Default_Alias'] - - def test__update_user_teams(self, backend, users): - u1, u2, u3 = users - - # Test user membership logic with regular expressions - backend.setting('TEAM_MAP')['Blue']['users'] = re.compile('.*') - backend.setting('TEAM_MAP')['Red']['users'] = re.compile('.*') - - desired_team_state = {} - teams_to_create = {} - _update_user_teams(backend, desired_team_state, teams_to_create, u1) - assert teams_to_create == {'Red': 'Default', 'Blue': 'Default'} - assert desired_team_state == {'Default': {'Blue': {'member_role': True}, 'Red': {'member_role': True}}} - - # Test remove feature enabled - backend.setting('TEAM_MAP')['Blue']['remove'] = True - backend.setting('TEAM_MAP')['Red']['remove'] = True - backend.setting('TEAM_MAP')['Blue']['users'] = '' - backend.setting('TEAM_MAP')['Red']['users'] = '' - - desired_team_state = {} - teams_to_create = {} - _update_user_teams(backend, desired_team_state, teams_to_create, u1) - assert teams_to_create == {'Red': 'Default', 'Blue': 'Default'} - assert desired_team_state == {'Default': {'Blue': {'member_role': False}, 'Red': {'member_role': False}}} - - # Test remove feature disabled - backend.setting('TEAM_MAP')['Blue']['remove'] = False - backend.setting('TEAM_MAP')['Red']['remove'] = False - - desired_team_state = {} - teams_to_create = {} - _update_user_teams(backend, desired_team_state, teams_to_create, u2) - assert teams_to_create == {'Red': 'Default', 'Blue': 'Default'} - # If we don't care about team memberships we just don't add them to the hash so this would be an empty hash - assert desired_team_state == {} - - -@pytest.mark.django_db -class TestSAMLM2M: - @pytest.mark.parametrize( - "expression, remove, expected_return", - [ - # No expression with no remove - (None, False, None), - ("", False, None), - # No expression with remove - (None, True, False), - # True expression with and without remove - (True, False, True), - (True, True, True), - # Single string matching the user name - ("user1", False, True), - # Single string matching the user email - ("user1@foo.com", False, True), - # Single string not matching username or email, no remove - ("user27", False, None), - # Single string not matching username or email, with remove - ("user27", True, False), - # Same tests with arrays instead of strings - (["user1"], False, True), - (["user1@foo.com"], False, True), - (["user27"], False, None), - (["user27"], True, False), - # Arrays with nothing matching - (["user27", "user28"], False, None), - (["user27", "user28"], True, False), - # Arrays with all matches - (["user1", "user1@foo.com"], False, True), - # Arrays with some match, some not - (["user1", "user28", "user27"], False, True), - # - # Note: For RE's, usually settings takes care of the compilation for us, so we have to do it manually for testing. - # we also need to remove any / or flags for the compile to happen - # - # Matching username regex non-array - (re.compile("^user.*"), False, True), - (re.compile("^user.*"), True, True), - # Matching email regex non-array - (re.compile(".*@foo.com$"), False, True), - (re.compile(".*@foo.com$"), True, True), - # Non-array not matching username or email - (re.compile("^$"), False, None), - (re.compile("^$"), True, False), - # All re tests just in array form - ([re.compile("^user.*")], False, True), - ([re.compile("^user.*")], True, True), - ([re.compile(".*@foo.com$")], False, True), - ([re.compile(".*@foo.com$")], True, True), - ([re.compile("^$")], False, None), - ([re.compile("^$")], True, False), - # An re with username matching but not email - ([re.compile("^user.*"), re.compile(".*@bar.com$")], False, True), - # An re with email matching but not username - ([re.compile("^user27$"), re.compile(".*@foo.com$")], False, True), - # An re array with no matching - ([re.compile("^user27$"), re.compile(".*@bar.com$")], False, None), - ([re.compile("^user27$"), re.compile(".*@bar.com$")], True, False), - # - # A mix of re and strings - # - # String matches, re does not - (["user1", re.compile(".*@bar.com$")], False, True), - # String does not match, re does - (["user27", re.compile(".*@foo.com$")], False, True), - # Nothing matches - (["user27", re.compile(".*@bar.com$")], False, None), - (["user27", re.compile(".*@bar.com$")], True, False), - ], - ) - def test__update_m2m_from_expression(self, expression, remove, expected_return): - user = User.objects.create(username='user1', last_name='foo', first_name='bar', email='user1@foo.com') - return_val = _update_m2m_from_expression(user, expression, remove) - assert return_val == expected_return - - -@pytest.mark.django_db -class TestSAMLAttrMaps: - @pytest.fixture - def backend(self): - class Backend: - s = { - 'ORGANIZATION_MAP': { - 'Default1': { - 'remove': True, - 'admins': 'foobar', - 'remove_admins': True, - 'users': 'foo', - 'remove_users': True, - 'organization_alias': 'o1_alias', - } - } - } - - def setting(self, key): - return self.s[key] - - return Backend() - - @pytest.mark.parametrize( - "setting, expected_state, expected_orgs_to_create, kwargs_member_of_mods", - [ - ( - # Default test, make sure that our roles get applied and removed as specified (with an alias) - { - 'saml_attr': 'memberOf', - 'saml_admin_attr': 'admins', - 'saml_auditor_attr': 'auditors', - 'remove': True, - 'remove_admins': True, - }, - { - 'Default2': {'member_role': True}, - 'Default3': {'admin_role': True}, - 'Default4': {'auditor_role': True}, - 'o1_alias': {'member_role': True}, - 'Rando1': {'admin_role': False, 'auditor_role': False, 'member_role': False}, - }, - [ - 'o1_alias', - 'Default2', - 'Default3', - 'Default4', - ], - None, - ), - ( - # Similar test, we are just going to override the values "coming from the IdP" to limit the teams - { - 'saml_attr': 'memberOf', - 'saml_admin_attr': 'admins', - 'saml_auditor_attr': 'auditors', - 'remove': True, - 'remove_admins': True, - }, - { - 'Default3': {'admin_role': True, 'member_role': True}, - 'Default4': {'auditor_role': True}, - 'Rando1': {'admin_role': False, 'auditor_role': False, 'member_role': False}, - }, - [ - 'Default3', - 'Default4', - ], - ['Default3'], - ), - ( - # Test to make sure the remove logic is working - { - 'saml_attr': 'memberOf', - 'saml_admin_attr': 'admins', - 'saml_auditor_attr': 'auditors', - 'remove': False, - 'remove_admins': False, - 'remove_auditors': False, - }, - { - 'Default2': {'member_role': True}, - 'Default3': {'admin_role': True}, - 'Default4': {'auditor_role': True}, - 'o1_alias': {'member_role': True}, - }, - [ - 'o1_alias', - 'Default2', - 'Default3', - 'Default4', - ], - ['Default1', 'Default2'], - ), - ], - ) - def test__update_user_orgs_by_saml_attr(self, backend, setting, expected_state, expected_orgs_to_create, kwargs_member_of_mods): - kwargs = { - 'username': u'cmeyers@redhat.com', - 'uid': 'idp:cmeyers@redhat.com', - 'request': {u'SAMLResponse': [], u'RelayState': [u'idp']}, - 'is_new': False, - 'response': { - 'session_index': '_0728f0e0-b766-0135-75fa-02842b07c044', - 'idp_name': u'idp', - 'attributes': { - 'memberOf': ['Default1', 'Default2'], - 'admins': ['Default3'], - 'auditors': ['Default4'], - 'groups': ['Blue', 'Red'], - 'User.email': ['cmeyers@redhat.com'], - 'User.LastName': ['Meyers'], - 'name_id': 'cmeyers@redhat.com', - 'User.FirstName': ['Chris'], - 'PersonImmutableID': [], - }, - }, - 'social': None, - 'strategy': None, - 'new_association': False, - } - if kwargs_member_of_mods: - kwargs['response']['attributes']['memberOf'] = kwargs_member_of_mods - - # Create a random organization in the database for testing - Organization.objects.create(name='Rando1') - - with override_settings(SOCIAL_AUTH_SAML_ORGANIZATION_ATTR=setting): - desired_org_state = {} - orgs_to_create = [] - _update_user_orgs_by_saml_attr(backend, desired_org_state, orgs_to_create, **kwargs) - assert desired_org_state == expected_state - assert orgs_to_create == expected_orgs_to_create - - @pytest.mark.parametrize( - "setting, expected_team_state, expected_teams_to_create, kwargs_group_override", - [ - ( - { - 'saml_attr': 'groups', - 'remove': False, - 'team_org_map': [ - {'team': 'Blue', 'organization': 'Default1'}, - {'team': 'Blue', 'organization': 'Default2'}, - {'team': 'Blue', 'organization': 'Default3'}, - {'team': 'Red', 'organization': 'Default1'}, - {'team': 'Green', 'organization': 'Default1'}, - {'team': 'Green', 'organization': 'Default3'}, - {'team': 'Yellow', 'team_alias': 'Yellow_Alias', 'organization': 'Default4', 'organization_alias': 'Default4_Alias'}, - ], - }, - { - 'Default1': { - 'Blue': {'member_role': True}, - 'Green': {'member_role': False}, - 'Red': {'member_role': True}, - }, - 'Default2': { - 'Blue': {'member_role': True}, - }, - 'Default3': { - 'Blue': {'member_role': True}, - 'Green': {'member_role': False}, - }, - 'Default4': { - 'Yellow': {'member_role': False}, - }, - }, - { - 'Blue': 'Default3', - 'Red': 'Default1', - }, - None, - ), - ( - { - 'saml_attr': 'groups', - 'remove': False, - 'team_org_map': [ - {'team': 'Blue', 'organization': 'Default1'}, - {'team': 'Blue', 'organization': 'Default2'}, - {'team': 'Blue', 'organization': 'Default3'}, - {'team': 'Red', 'organization': 'Default1'}, - {'team': 'Green', 'organization': 'Default1'}, - {'team': 'Green', 'organization': 'Default3'}, - {'team': 'Yellow', 'team_alias': 'Yellow_Alias', 'organization': 'Default4', 'organization_alias': 'Default4_Alias'}, - ], - }, - { - 'Default1': { - 'Blue': {'member_role': True}, - 'Green': {'member_role': True}, - 'Red': {'member_role': True}, - }, - 'Default2': { - 'Blue': {'member_role': True}, - }, - 'Default3': { - 'Blue': {'member_role': True}, - 'Green': {'member_role': True}, - }, - 'Default4': { - 'Yellow': {'member_role': False}, - }, - }, - { - 'Blue': 'Default3', - 'Red': 'Default1', - 'Green': 'Default3', - }, - ['Blue', 'Red', 'Green'], - ), - ( - { - 'saml_attr': 'groups', - 'remove': True, - 'team_org_map': [ - {'team': 'Blue', 'organization': 'Default1'}, - {'team': 'Blue', 'organization': 'Default2'}, - {'team': 'Blue', 'organization': 'Default3'}, - {'team': 'Red', 'organization': 'Default1'}, - {'team': 'Green', 'organization': 'Default1'}, - {'team': 'Green', 'organization': 'Default3'}, - {'team': 'Yellow', 'team_alias': 'Yellow_Alias', 'organization': 'Default4', 'organization_alias': 'Default4_Alias'}, - ], - }, - { - 'Default1': { - 'Blue': {'member_role': False}, - 'Green': {'member_role': True}, - 'Red': {'member_role': False}, - }, - 'Default2': { - 'Blue': {'member_role': False}, - }, - 'Default3': { - 'Blue': {'member_role': False}, - 'Green': {'member_role': True}, - }, - 'Default4': { - 'Yellow': {'member_role': False}, - }, - 'Rando1': { - 'Rando1': {'member_role': False}, - }, - }, - { - 'Green': 'Default3', - }, - ['Green'], - ), - ], - ) - def test__update_user_teams_by_saml_attr(self, setting, expected_team_state, expected_teams_to_create, kwargs_group_override): - kwargs = { - 'username': u'cmeyers@redhat.com', - 'uid': 'idp:cmeyers@redhat.com', - 'request': {u'SAMLResponse': [], u'RelayState': [u'idp']}, - 'is_new': False, - 'response': { - 'session_index': '_0728f0e0-b766-0135-75fa-02842b07c044', - 'idp_name': u'idp', - 'attributes': { - 'memberOf': ['Default1', 'Default2'], - 'admins': ['Default3'], - 'auditors': ['Default4'], - 'groups': ['Blue', 'Red'], - 'User.email': ['cmeyers@redhat.com'], - 'User.LastName': ['Meyers'], - 'name_id': 'cmeyers@redhat.com', - 'User.FirstName': ['Chris'], - 'PersonImmutableID': [], - }, - }, - 'social': None, - 'strategy': None, - 'new_association': False, - } - if kwargs_group_override: - kwargs['response']['attributes']['groups'] = kwargs_group_override - - o = Organization.objects.create(name='Rando1') - Team.objects.create(name='Rando1', organization_id=o.id) - - with override_settings(SOCIAL_AUTH_SAML_TEAM_ATTR=setting): - desired_team_state = {} - teams_to_create = {} - _update_user_teams_by_saml_attr(desired_team_state, teams_to_create, **kwargs) - assert desired_team_state == expected_team_state - assert teams_to_create == expected_teams_to_create - - -@pytest.mark.django_db -class TestSAMLUserFlags: - @pytest.mark.parametrize( - "user_flags_settings, expected, is_superuser", - [ - # In this case we will pass no user flags so new_flag should be false and changed will def be false - ( - {}, - (False, False), - False, - ), - # NOTE: The first handful of tests test role/value as string instead of lists. - # This was from the initial implementation of these fields but the code should be able to handle this - # There are a couple tests at the end of this which will validate arrays in these values. - # - # In this case we will give the user a group to make them an admin - ( - {'is_superuser_role': 'test-role-1'}, - (True, True), - False, - ), - # In this case we will give the user a flag that will make then an admin - ( - {'is_superuser_attr': 'is_superuser'}, - (True, True), - False, - ), - # In this case we will give the user a flag but the wrong value - ( - {'is_superuser_attr': 'is_superuser', 'is_superuser_value': 'junk'}, - (False, False), - False, - ), - # In this case we will give the user a flag and the right value - ( - {'is_superuser_attr': 'is_superuser', 'is_superuser_value': 'true'}, - (True, True), - False, - ), - # In this case we will give the user a proper role and an is_superuser_attr role that they don't have, this should make them an admin - ( - {'is_superuser_role': 'test-role-1', 'is_superuser_attr': 'gibberish', 'is_superuser_value': 'true'}, - (True, True), - False, - ), - # In this case we will give the user a proper role and an is_superuser_attr role that they have, this should make them an admin - ( - {'is_superuser_role': 'test-role-1', 'is_superuser_attr': 'test-role-1'}, - (True, True), - False, - ), - # In this case we will give the user a proper role and an is_superuser_attr role that they have but a bad value, this should make them an admin - ( - {'is_superuser_role': 'test-role-1', 'is_superuser_attr': 'is_superuser', 'is_superuser_value': 'junk'}, - (False, False), - False, - ), - # In this case we will give the user everything - ( - {'is_superuser_role': 'test-role-1', 'is_superuser_attr': 'is_superuser', 'is_superuser_value': 'true'}, - (True, True), - False, - ), - # In this test case we will validate that a single attribute (instead of a list) still works - ( - {'is_superuser_attr': 'name_id', 'is_superuser_value': 'test_id'}, - (True, True), - False, - ), - # This will be a negative test for a single attribute - ( - {'is_superuser_attr': 'name_id', 'is_superuser_value': 'junk'}, - (False, False), - False, - ), - # The user is already a superuser so we should remove them - ( - {'is_superuser_attr': 'name_id', 'is_superuser_value': 'junk', 'remove_superusers': True}, - (False, True), - True, - ), - # The user is already a superuser but we don't have a remove field - ( - {'is_superuser_attr': 'name_id', 'is_superuser_value': 'junk', 'remove_superusers': False}, - (True, False), - True, - ), - # Positive test for multiple values for is_superuser_value - ( - {'is_superuser_attr': 'is_superuser', 'is_superuser_value': ['junk', 'junk2', 'else', 'junk']}, - (True, True), - False, - ), - # Negative test for multiple values for is_superuser_value - ( - {'is_superuser_attr': 'is_superuser', 'is_superuser_value': ['junk', 'junk2', 'junk']}, - (False, True), - True, - ), - # Positive test for multiple values of is_superuser_role - ( - {'is_superuser_role': ['junk', 'junk2', 'something', 'junk']}, - (True, True), - False, - ), - # Negative test for multiple values of is_superuser_role - ( - {'is_superuser_role': ['junk', 'junk2', 'junk']}, - (False, True), - True, - ), - ], - ) - def test__check_flag(self, user_flags_settings, expected, is_superuser): - user = User() - user.username = 'John' - user.is_superuser = is_superuser - - attributes = { - 'email': ['noone@nowhere.com'], - 'last_name': ['Westcott'], - 'is_superuser': ['something', 'else', 'true'], - 'username': ['test_id'], - 'first_name': ['John'], - 'Role': ['test-role-1', 'something', 'different'], - 'name_id': 'test_id', - } - - assert expected == _check_flag(user, 'superuser', attributes, user_flags_settings) diff --git a/awx/sso/tests/functional/test_social_base_pipeline.py b/awx/sso/tests/functional/test_social_base_pipeline.py deleted file mode 100644 index 38a49e15f331..000000000000 --- a/awx/sso/tests/functional/test_social_base_pipeline.py +++ /dev/null @@ -1,76 +0,0 @@ -import pytest - -from awx.main.models import User -from awx.sso.social_base_pipeline import AuthNotFound, check_user_found_or_created, set_is_active_for_new_user, prevent_inactive_login, AuthInactive - - -@pytest.mark.django_db -class TestSocialBasePipeline: - def test_check_user_found_or_created_no_exception(self): - # If we have a user (the True param, we should not get an exception) - try: - check_user_found_or_created(None, {}, True) - except AuthNotFound: - assert False, 'check_user_found_or_created should not have raised an exception with a user' - - @pytest.mark.parametrize( - "details, kwargs, expected_id", - [ - ( - {}, - {}, - '???', - ), - ( - {}, - {'uid': 'kwargs_uid'}, - 'kwargs_uid', - ), - ( - {}, - {'uid': 'kwargs_uid', 'email': 'kwargs_email'}, - 'kwargs_email', - ), - ( - {'email': 'details_email'}, - {'uid': 'kwargs_uid', 'email': 'kwargs_email'}, - 'details_email', - ), - ], - ) - def test_check_user_found_or_created_exceptions(self, details, expected_id, kwargs): - with pytest.raises(AuthNotFound) as e: - check_user_found_or_created(None, details, False, None, **kwargs) - assert f'An account cannot be found for {expected_id}' == str(e.value) - - @pytest.mark.parametrize( - "kwargs, expected_details, expected_response", - [ - ({}, {}, None), - ({'is_new': False}, {}, None), - ({'is_new': True}, {'is_active': True}, {'details': {'is_active': True}}), - ], - ) - def test_set_is_active_for_new_user(self, kwargs, expected_details, expected_response): - details = {} - response = set_is_active_for_new_user(None, details, None, None, **kwargs) - assert details == expected_details - assert response == expected_response - - def test_prevent_inactive_login_no_exception_no_user(self): - try: - prevent_inactive_login(None, None, None, None, None) - except AuthInactive: - assert False, 'prevent_inactive_login should not have raised an exception with no user' - - def test_prevent_inactive_login_no_exception_active_user(self): - user = User.objects.create(username='user1@foo.com', last_name='foo', first_name='bar', email='user1@foo.com', is_active=True) - try: - prevent_inactive_login(None, None, user, None, None) - except AuthInactive: - assert False, 'prevent_inactive_login should not have raised an exception with an active user' - - def test_prevent_inactive_login_no_exception_inactive_user(self): - user = User.objects.create(username='user1@foo.com', last_name='foo', first_name='bar', email='user1@foo.com', is_active=False) - with pytest.raises(AuthInactive): - prevent_inactive_login(None, None, user, None, None) diff --git a/awx/sso/tests/functional/test_social_pipeline.py b/awx/sso/tests/functional/test_social_pipeline.py deleted file mode 100644 index f26886e71944..000000000000 --- a/awx/sso/tests/functional/test_social_pipeline.py +++ /dev/null @@ -1,113 +0,0 @@ -import pytest -import re - -from awx.sso.social_pipeline import update_user_orgs, update_user_teams -from awx.main.models import User, Team, Organization - - -@pytest.fixture -def users(): - u1 = User.objects.create(username='user1@foo.com', last_name='foo', first_name='bar', email='user1@foo.com') - u2 = User.objects.create(username='user2@foo.com', last_name='foo', first_name='bar', email='user2@foo.com') - u3 = User.objects.create(username='user3@foo.com', last_name='foo', first_name='bar', email='user3@foo.com') - return (u1, u2, u3) - - -@pytest.mark.django_db -class TestSocialPipeline: - @pytest.fixture - def backend(self): - class Backend: - s = { - 'ORGANIZATION_MAP': { - 'Default': { - 'remove': True, - 'admins': 'foobar', - 'remove_admins': True, - 'users': 'foo', - 'remove_users': True, - 'organization_alias': '', - } - }, - 'TEAM_MAP': {'Blue': {'organization': 'Default', 'remove': True, 'users': ''}, 'Red': {'organization': 'Default', 'remove': True, 'users': ''}}, - } - - def setting(self, key): - return self.s[key] - - return Backend() - - @pytest.fixture - def org(self): - return Organization.objects.create(name="Default") - - def test_update_user_orgs(self, org, backend, users): - u1, u2, u3 = users - - # Test user membership logic with regular expressions - backend.setting('ORGANIZATION_MAP')['Default']['admins'] = re.compile('.*') - backend.setting('ORGANIZATION_MAP')['Default']['users'] = re.compile('.*') - - update_user_orgs(backend, None, u1) - update_user_orgs(backend, None, u2) - update_user_orgs(backend, None, u3) - - assert org.admin_role.members.count() == 3 - assert org.member_role.members.count() == 3 - - # Test remove feature enabled - backend.setting('ORGANIZATION_MAP')['Default']['admins'] = '' - backend.setting('ORGANIZATION_MAP')['Default']['users'] = '' - backend.setting('ORGANIZATION_MAP')['Default']['remove_admins'] = True - backend.setting('ORGANIZATION_MAP')['Default']['remove_users'] = True - update_user_orgs(backend, None, u1) - - assert org.admin_role.members.count() == 2 - assert org.member_role.members.count() == 2 - - # Test remove feature disabled - backend.setting('ORGANIZATION_MAP')['Default']['remove_admins'] = False - backend.setting('ORGANIZATION_MAP')['Default']['remove_users'] = False - update_user_orgs(backend, None, u2) - - assert org.admin_role.members.count() == 2 - assert org.member_role.members.count() == 2 - - # Test organization alias feature - backend.setting('ORGANIZATION_MAP')['Default']['organization_alias'] = 'Default_Alias' - update_user_orgs(backend, None, u1) - assert Organization.objects.get(name="Default_Alias") is not None - - def test_update_user_teams(self, backend, users): - u1, u2, u3 = users - - # Test user membership logic with regular expressions - backend.setting('TEAM_MAP')['Blue']['users'] = re.compile('.*') - backend.setting('TEAM_MAP')['Red']['users'] = re.compile('.*') - - update_user_teams(backend, None, u1) - update_user_teams(backend, None, u2) - update_user_teams(backend, None, u3) - - assert Team.objects.get(name="Red").member_role.members.count() == 3 - assert Team.objects.get(name="Blue").member_role.members.count() == 3 - - # Test remove feature enabled - backend.setting('TEAM_MAP')['Blue']['remove'] = True - backend.setting('TEAM_MAP')['Red']['remove'] = True - backend.setting('TEAM_MAP')['Blue']['users'] = '' - backend.setting('TEAM_MAP')['Red']['users'] = '' - - update_user_teams(backend, None, u1) - - assert Team.objects.get(name="Red").member_role.members.count() == 2 - assert Team.objects.get(name="Blue").member_role.members.count() == 2 - - # Test remove feature disabled - backend.setting('TEAM_MAP')['Blue']['remove'] = False - backend.setting('TEAM_MAP')['Red']['remove'] = False - - update_user_teams(backend, None, u2) - - assert Team.objects.get(name="Red").member_role.members.count() == 2 - assert Team.objects.get(name="Blue").member_role.members.count() == 2 diff --git a/awx/sso/tests/test_env.py b/awx/sso/tests/test_env.py deleted file mode 100644 index b63da8ed8a16..000000000000 --- a/awx/sso/tests/test_env.py +++ /dev/null @@ -1,4 +0,0 @@ -# Ensure that our autouse overwrites are working -def test_cache(settings): - assert settings.CACHES['default']['BACKEND'] == 'django.core.cache.backends.locmem.LocMemCache' - assert settings.CACHES['default']['LOCATION'].startswith('unique-') diff --git a/awx/sso/tests/unit/test_fields.py b/awx/sso/tests/unit/test_fields.py deleted file mode 100644 index 35ab58d07fab..000000000000 --- a/awx/sso/tests/unit/test_fields.py +++ /dev/null @@ -1,235 +0,0 @@ -import pytest -from unittest import mock - -from rest_framework.exceptions import ValidationError - -from awx.sso.fields import SAMLOrgAttrField, SAMLTeamAttrField, SAMLUserFlagsAttrField, LDAPGroupTypeParamsField, LDAPServerURIField - - -class TestSAMLOrgAttrField: - @pytest.mark.parametrize( - "data, expected", - [ - ({}, {}), - ({'remove': True, 'saml_attr': 'foobar'}, {'remove': True, 'saml_attr': 'foobar'}), - ({'remove': True, 'saml_attr': 1234}, {'remove': True, 'saml_attr': '1234'}), - ({'remove': True, 'saml_attr': 3.14}, {'remove': True, 'saml_attr': '3.14'}), - ({'saml_attr': 'foobar'}, {'saml_attr': 'foobar'}), - ({'remove': True}, {'remove': True}), - ({'remove': True, 'saml_admin_attr': 'foobar'}, {'remove': True, 'saml_admin_attr': 'foobar'}), - ({'saml_admin_attr': 'foobar'}, {'saml_admin_attr': 'foobar'}), - ({'remove_admins': True, 'saml_admin_attr': 'foobar'}, {'remove_admins': True, 'saml_admin_attr': 'foobar'}), - ( - {'remove': True, 'saml_attr': 'foo', 'remove_admins': True, 'saml_admin_attr': 'bar'}, - {'remove': True, 'saml_attr': 'foo', 'remove_admins': True, 'saml_admin_attr': 'bar'}, - ), - ], - ) - def test_internal_value_valid(self, data, expected): - field = SAMLOrgAttrField() - res = field.to_internal_value(data) - assert res == expected - - @pytest.mark.parametrize( - "data, expected", - [ - ({'remove': 'blah', 'saml_attr': 'foobar'}, {'remove': ['Must be a valid boolean.']}), - ({'remove': True, 'saml_attr': False}, {'saml_attr': ['Not a valid string.']}), - ( - {'remove': True, 'saml_attr': False, 'foo': 'bar', 'gig': 'ity'}, - {'saml_attr': ['Not a valid string.'], 'foo': ['Invalid field.'], 'gig': ['Invalid field.']}, - ), - ({'remove_admins': True, 'saml_admin_attr': False}, {'saml_admin_attr': ['Not a valid string.']}), - ({'remove_admins': 'blah', 'saml_admin_attr': 'foobar'}, {'remove_admins': ['Must be a valid boolean.']}), - ], - ) - def test_internal_value_invalid(self, data, expected): - field = SAMLOrgAttrField() - with pytest.raises(ValidationError) as e: - field.to_internal_value(data) - assert e.value.detail == expected - - -class TestSAMLTeamAttrField: - @pytest.mark.parametrize( - "data", - [ - {}, - {'remove': True, 'saml_attr': 'foobar', 'team_org_map': []}, - {'remove': True, 'saml_attr': 'foobar', 'team_org_map': [{'team': 'Engineering', 'organization': 'Ansible'}]}, - { - 'remove': True, - 'saml_attr': 'foobar', - 'team_org_map': [ - {'team': 'Engineering', 'organization': 'Ansible'}, - {'team': 'Engineering', 'organization': 'Ansible2'}, - {'team': 'Engineering2', 'organization': 'Ansible'}, - ], - }, - { - 'remove': True, - 'saml_attr': 'foobar', - 'team_org_map': [ - {'team': 'Engineering', 'organization': 'Ansible'}, - {'team': 'Engineering', 'organization': 'Ansible2'}, - {'team': 'Engineering2', 'organization': 'Ansible'}, - ], - }, - { - 'remove': True, - 'saml_attr': 'foobar', - 'team_org_map': [ - {'team': 'Engineering', 'team_alias': 'Engineering Team', 'organization': 'Ansible'}, - {'team': 'Engineering', 'organization': 'Ansible2'}, - {'team': 'Engineering2', 'organization': 'Ansible'}, - ], - }, - ], - ) - def test_internal_value_valid(self, data): - field = SAMLTeamAttrField() - res = field.to_internal_value(data) - assert res == data - - @pytest.mark.parametrize( - "data, expected", - [ - ( - {'remove': True, 'saml_attr': 'foobar', 'team_org_map': [{'team': 'foobar', 'not_a_valid_key': 'blah', 'organization': 'Ansible'}]}, - {'team_org_map': {0: {'not_a_valid_key': ['Invalid field.']}}}, - ), - ( - {'remove': False, 'saml_attr': 'foobar', 'team_org_map': [{'organization': 'Ansible'}]}, - {'team_org_map': {0: {'team': ['This field is required.']}}}, - ), - ( - {'remove': False, 'saml_attr': 'foobar', 'team_org_map': [{}]}, - {'team_org_map': {0: {'organization': ['This field is required.'], 'team': ['This field is required.']}}}, - ), - ], - ) - def test_internal_value_invalid(self, data, expected): - field = SAMLTeamAttrField() - with pytest.raises(ValidationError) as e: - field.to_internal_value(data) - assert e.value.detail == expected - - -class TestSAMLUserFlagsAttrField: - @pytest.mark.parametrize( - "data", - [ - {}, - {'is_superuser_attr': 'something'}, - {'is_superuser_value': ['value']}, - {'is_superuser_role': ['my_peeps']}, - {'remove_superusers': False}, - {'is_system_auditor_attr': 'something_else'}, - {'is_system_auditor_value': ['value2']}, - {'is_system_auditor_role': ['other_peeps']}, - {'remove_system_auditors': False}, - ], - ) - def test_internal_value_valid(self, data): - field = SAMLUserFlagsAttrField() - res = field.to_internal_value(data) - assert res == data - - @pytest.mark.parametrize( - "data, expected", - [ - ( - { - 'junk': 'something', - 'is_superuser_value': 'value', - 'is_superuser_role': 'my_peeps', - 'is_system_auditor_attr': 'else', - 'is_system_auditor_value': 'value2', - 'is_system_auditor_role': 'other_peeps', - }, - { - 'junk': ['Invalid field.'], - 'is_superuser_role': ['Expected a list of items but got type "str".'], - 'is_superuser_value': ['Expected a list of items but got type "str".'], - 'is_system_auditor_role': ['Expected a list of items but got type "str".'], - 'is_system_auditor_value': ['Expected a list of items but got type "str".'], - }, - ), - ( - { - 'junk': 'something', - }, - { - 'junk': ['Invalid field.'], - }, - ), - ( - { - 'junk': 'something', - 'junk2': 'else', - }, - { - 'junk': ['Invalid field.'], - 'junk2': ['Invalid field.'], - }, - ), - # make sure we can't pass a string to the boolean fields - ( - { - 'remove_superusers': 'test', - 'remove_system_auditors': 'test', - }, - { - "remove_superusers": ["Must be a valid boolean."], - "remove_system_auditors": ["Must be a valid boolean."], - }, - ), - ], - ) - def test_internal_value_invalid(self, data, expected): - field = SAMLUserFlagsAttrField() - with pytest.raises(ValidationError) as e: - field.to_internal_value(data) - print(e.value.detail) - assert e.value.detail == expected - - -class TestLDAPGroupTypeParamsField: - @pytest.mark.parametrize( - "group_type, data, expected", - [ - ('LDAPGroupType', {'name_attr': 'user', 'bob': ['a', 'b'], 'scooter': 'hello'}, ['Invalid key(s): "bob", "scooter".']), - ('MemberDNGroupType', {'name_attr': 'user', 'member_attr': 'west', 'bob': ['a', 'b'], 'scooter': 'hello'}, ['Invalid key(s): "bob", "scooter".']), - ( - 'PosixUIDGroupType', - {'name_attr': 'user', 'member_attr': 'west', 'ldap_group_user_attr': 'legacyThing', 'bob': ['a', 'b'], 'scooter': 'hello'}, - ['Invalid key(s): "bob", "member_attr", "scooter".'], - ), - ], - ) - def test_internal_value_invalid(self, group_type, data, expected): - field = LDAPGroupTypeParamsField() - field.get_depends_on = mock.MagicMock(return_value=group_type) - - with pytest.raises(ValidationError) as e: - field.to_internal_value(data) - assert e.value.detail == expected - - -class TestLDAPServerURIField: - @pytest.mark.parametrize( - "ldap_uri, exception, expected", - [ - (r'ldap://servername.com:444', None, r'ldap://servername.com:444'), - (r'ldap://servername.so3:444', None, r'ldap://servername.so3:444'), - (r'ldaps://servername3.s300:344', None, r'ldaps://servername3.s300:344'), - (r'ldap://servername.-so3:444', ValidationError, None), - ], - ) - def test_run_validators_valid(self, ldap_uri, exception, expected): - field = LDAPServerURIField() - if exception is None: - assert field.run_validators(ldap_uri) == expected - else: - with pytest.raises(exception): - field.run_validators(ldap_uri) diff --git a/awx/sso/tests/unit/test_ldap.py b/awx/sso/tests/unit/test_ldap.py deleted file mode 100644 index 300102934f79..000000000000 --- a/awx/sso/tests/unit/test_ldap.py +++ /dev/null @@ -1,25 +0,0 @@ -import ldap - -from awx.sso.backends import LDAPSettings -from awx.sso.validators import validate_ldap_filter -from django.core.cache import cache - - -def test_ldap_default_settings(mocker): - from_db = mocker.Mock(**{'order_by.return_value': []}) - with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=from_db): - settings = LDAPSettings() - assert settings.ORGANIZATION_MAP == {} - assert settings.TEAM_MAP == {} - - -def test_ldap_default_network_timeout(mocker): - cache.clear() # clearing cache avoids picking up stray default for OPT_REFERRALS - from_db = mocker.Mock(**{'order_by.return_value': []}) - with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=from_db): - settings = LDAPSettings() - assert settings.CONNECTION_OPTIONS[ldap.OPT_NETWORK_TIMEOUT] == 30 - - -def test_ldap_filter_validator(): - validate_ldap_filter('(test-uid=%(user)s)', with_user=True) diff --git a/awx/sso/tests/unit/test_pipelines.py b/awx/sso/tests/unit/test_pipelines.py deleted file mode 100644 index 94a1111187b8..000000000000 --- a/awx/sso/tests/unit/test_pipelines.py +++ /dev/null @@ -1,12 +0,0 @@ -import pytest - - -@pytest.mark.parametrize( - "lib", - [ - ("saml_pipeline"), - ("social_pipeline"), - ], -) -def test_module_loads(lib): - module = __import__("awx.sso." + lib) # noqa diff --git a/awx/sso/tests/unit/test_tacacsplus.py b/awx/sso/tests/unit/test_tacacsplus.py deleted file mode 100644 index 60ed0c47995d..000000000000 --- a/awx/sso/tests/unit/test_tacacsplus.py +++ /dev/null @@ -1,49 +0,0 @@ -from unittest import mock - - -def test_empty_host_fails_auth(tacacsplus_backend): - with mock.patch('awx.sso.backends.django_settings') as settings: - settings.TACACSPLUS_HOST = '' - ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass") - assert ret_user is None - - -def test_client_raises_exception(tacacsplus_backend): - client = mock.MagicMock() - client.authenticate.side_effect = Exception("foo") - with mock.patch('awx.sso.backends.django_settings') as settings, mock.patch('awx.sso.backends.logger') as logger, mock.patch( - 'tacacs_plus.TACACSClient', return_value=client - ): - settings.TACACSPLUS_HOST = 'localhost' - settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii' - ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass") - assert ret_user is None - logger.exception.assert_called_once_with("TACACS+ Authentication Error: foo") - - -def test_client_return_invalid_fails_auth(tacacsplus_backend): - auth = mock.MagicMock() - auth.valid = False - client = mock.MagicMock() - client.authenticate.return_value = auth - with mock.patch('awx.sso.backends.django_settings') as settings, mock.patch('tacacs_plus.TACACSClient', return_value=client): - settings.TACACSPLUS_HOST = 'localhost' - settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii' - ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass") - assert ret_user is None - - -def test_client_return_valid_passes_auth(tacacsplus_backend): - auth = mock.MagicMock() - auth.valid = True - client = mock.MagicMock() - client.authenticate.return_value = auth - user = mock.MagicMock() - user.has_usable_password = mock.MagicMock(return_value=False) - with mock.patch('awx.sso.backends.django_settings') as settings, mock.patch('tacacs_plus.TACACSClient', return_value=client), mock.patch( - 'awx.sso.backends._get_or_set_enterprise_user', return_value=user - ): - settings.TACACSPLUS_HOST = 'localhost' - settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii' - ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass") - assert ret_user == user diff --git a/awx/sso/urls.py b/awx/sso/urls.py deleted file mode 100644 index 93da0996c970..000000000000 --- a/awx/sso/urls.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -from django.urls import re_path - -from awx.sso.views import sso_complete, sso_error, sso_inactive, saml_metadata - - -app_name = 'sso' -urlpatterns = [ - re_path(r'^complete/$', sso_complete, name='sso_complete'), - re_path(r'^error/$', sso_error, name='sso_error'), - re_path(r'^inactive/$', sso_inactive, name='sso_inactive'), - re_path(r'^metadata/saml/$', saml_metadata, name='saml_metadata'), -] diff --git a/awx/sso/validators.py b/awx/sso/validators.py deleted file mode 100644 index 478b86b36fc9..000000000000 --- a/awx/sso/validators.py +++ /dev/null @@ -1,74 +0,0 @@ -# Python -import re - -# Python-LDAP -import ldap - -# Django -from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ - -__all__ = [ - 'validate_ldap_dn', - 'validate_ldap_dn_with_user', - 'validate_ldap_bind_dn', - 'validate_ldap_filter', - 'validate_ldap_filter_with_user', - 'validate_tacacsplus_disallow_nonascii', -] - - -def validate_ldap_dn(value, with_user=False): - if with_user: - if '%(user)s' not in value: - raise ValidationError(_('DN must include "%%(user)s" placeholder for username: %s') % value) - dn_value = value.replace('%(user)s', 'USER') - else: - dn_value = value - try: - ldap.dn.str2dn(dn_value.encode('utf-8')) - except ldap.DECODING_ERROR: - raise ValidationError(_('Invalid DN: %s') % value) - - -def validate_ldap_dn_with_user(value): - validate_ldap_dn(value, with_user=True) - - -def validate_ldap_bind_dn(value): - if not re.match(r'^[A-Za-z][A-Za-z0-9._-]*?\\[A-Za-z0-9 ._-]+?$', value.strip()) and not re.match( - r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', value.strip() - ): - validate_ldap_dn(value) - - -def validate_ldap_filter(value, with_user=False): - value = value.strip() - if not value: - return - if with_user: - if '%(user)s' not in value: - raise ValidationError(_('DN must include "%%(user)s" placeholder for username: %s') % value) - dn_value = value.replace('%(user)s', 'USER') - else: - dn_value = value - if re.match(r'^\([A-Za-z0-9-]+?=[^()]+?\)$', dn_value): - return - elif re.match(r'^\([&|!]\(.*?\)\)$', dn_value): - try: - map(validate_ldap_filter, ['(%s)' % x for x in dn_value[3:-2].split(')(')]) - return - except ValidationError: - pass - raise ValidationError(_('Invalid filter: %s') % value) - - -def validate_ldap_filter_with_user(value): - validate_ldap_filter(value, with_user=True) - - -def validate_tacacsplus_disallow_nonascii(value): - try: - value.encode('ascii') - except (UnicodeEncodeError, UnicodeDecodeError): - raise ValidationError(_('TACACS+ secret does not allow non-ascii characters')) diff --git a/awx/sso/views.py b/awx/sso/views.py deleted file mode 100644 index c4ecdc763239..000000000000 --- a/awx/sso/views.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import urllib.parse -import logging - -# Django -from django.urls import reverse -from django.http import HttpResponse -from django.views.generic import View -from django.views.generic.base import RedirectView -from django.utils.encoding import smart_str -from django.conf import settings - -logger = logging.getLogger('awx.sso.views') - - -class BaseRedirectView(RedirectView): - permanent = True - - def get_redirect_url(self, *args, **kwargs): - last_path = self.request.COOKIES.get('lastPath', '') - last_path = urllib.parse.quote(urllib.parse.unquote(last_path).strip('"')) - url = reverse('ui:index') - if last_path: - return '%s#%s' % (url, last_path) - else: - return url - - -sso_error = BaseRedirectView.as_view() -sso_inactive = BaseRedirectView.as_view() - - -class CompleteView(BaseRedirectView): - def dispatch(self, request, *args, **kwargs): - response = super(CompleteView, self).dispatch(request, *args, **kwargs) - if self.request.user and self.request.user.is_authenticated: - logger.info(smart_str(u"User {} logged in".format(self.request.user.username))) - response.set_cookie('userLoggedIn', 'true') - response.setdefault('X-API-Session-Cookie-Name', getattr(settings, 'SESSION_COOKIE_NAME', 'awx_sessionid')) - return response - - -sso_complete = CompleteView.as_view() - - -class MetadataView(View): - def get(self, request, *args, **kwargs): - from social_django.utils import load_backend, load_strategy - - complete_url = reverse('social:complete', args=('saml',)) - try: - saml_backend = load_backend(load_strategy(request), 'saml', redirect_uri=complete_url) - metadata, errors = saml_backend.generate_metadata_xml() - except Exception as e: - logger.exception('unable to generate SAML metadata') - errors = e - if not errors: - return HttpResponse(content=metadata, content_type='text/xml') - else: - return HttpResponse(content=str(errors), content_type='text/plain') - - -saml_metadata = MetadataView.as_view() diff --git a/awx/static/RedHatDisplay-Medium.ttf b/awx/static/RedHatDisplay-Medium.ttf new file mode 100644 index 000000000000..a3adad30a5fd Binary files /dev/null and b/awx/static/RedHatDisplay-Medium.ttf differ diff --git a/awx/static/RedHatDisplay-Regular.ttf b/awx/static/RedHatDisplay-Regular.ttf new file mode 100644 index 000000000000..546554484b68 Binary files /dev/null and b/awx/static/RedHatDisplay-Regular.ttf differ diff --git a/awx/static/api/api.js b/awx/static/api/api.js index 67053ae2f626..98d803ad9f0c 100644 --- a/awx/static/api/api.js +++ b/awx/static/api/api.js @@ -14,7 +14,7 @@ $(function() { $('span.str').each(function() { var s = $(this).html(); if (s.match(/^\"\/.+\/\"$/) || s.match(/^\"\/.+\/\?.*\"$/)) { - $(this).html('"' + s.replace(/\"/g, '') + '"'); + $(this).html('"' + s.replaceAll('"', '') + '"'); } }); @@ -27,7 +27,7 @@ $(function() { }).each(function() { $(this).nextUntil('span.pun:contains("]")').filter('span.str').each(function() { if ($(this).text().match(/^\".+\"$/)) { - var s = $(this).text().replace(/\"/g, ''); + var s = $(this).text().replaceAll('"', ''); $(this).html('"' + s + '"'); } else if ($(this).text() !== '"') { diff --git a/awx/static/awx-spud-reading.svg b/awx/static/awx-spud-reading.svg new file mode 100644 index 000000000000..9e1a02dffff8 --- /dev/null +++ b/awx/static/awx-spud-reading.svg @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/static/custom_404.html b/awx/static/custom_404.html new file mode 100644 index 000000000000..9e22cc0767d3 --- /dev/null +++ b/awx/static/custom_404.html @@ -0,0 +1,8 @@ + + + + Redirecting + + + Redirecting + diff --git a/awx/static/custom_502.html b/awx/static/custom_502.html new file mode 100644 index 000000000000..5fc541245a3c --- /dev/null +++ b/awx/static/custom_502.html @@ -0,0 +1,21 @@ + + + + On Break... + + + + +
+
+ AWX mascot reading a book + 502 +
+
+
HTTP Response: 502
+
The spud is taking a much needed break...
+
Please check back later.
+
+
+ + diff --git a/awx/static/custom_504.html b/awx/static/custom_504.html new file mode 100644 index 000000000000..5f39b7f28d6b --- /dev/null +++ b/awx/static/custom_504.html @@ -0,0 +1,21 @@ + + + + On Break... + + + + +
+
+ AWX mascot reading a book + 504 +
+
+
HTTP Response: 504
+
The spud is taking a much needed break...
+
Please check back later.
+
+
+ + diff --git a/awx/static/custom_error.css b/awx/static/custom_error.css new file mode 100644 index 000000000000..94401bb3937b --- /dev/null +++ b/awx/static/custom_error.css @@ -0,0 +1,81 @@ +@font-face { + font-family: redhat-display-medium; + src: url('./RedHatDisplay-Medium.ttf'); +} + +@font-face { + font-family: redhat-display-regular; + src: url('./RedHatDisplay-Regular.ttf'); +} + +html, body { + height:100%; + width:100%; +} + +body { + background-color: #F0F0F0; +} + +.container { + position: absolute; + top: 24px; + right: 24px; + bottom: 24px; + left: 24px; +} + +.upper_div { + background-color: #F8EBA7; + justify-content: center; + text-align: center; + height: 50%; + align-items: flex-end; + display: flex; + min-height: 450px; + width: 100%; +} + +.main_image { + height: 450px; + width:auto; +} + +.error_number { + position: absolute; + top: 23px; + right: 90px; + font-size:200px; + color: #FDBA48; + font-family: Impact, Haettenschweiler, "Franklin Gothic Bold", Charcoal, "Helvetica Inserat", "Bitstream Vera Sans Bold", "Arial Black", sans-serif; +} + +.message_div { + background-color: #FFFFFF; + border: 1px solid #D2D2D2; + text-align: center; + height: 50%; + vertical-align: top; +} + +.m1,.m2,.m3 { + color: #151515; + width: 100%; + font-family: redhat-display-medium, sans-serif; +} + +.m1 { + font-size: 24px; + padding-top: 24px; +} + +.m2 { + font-size: 20px; + padding-top: 20px; +} + +.m3 { + font-size: 16px; + padding-top: 20px; + font-family: redhat-display-regular, sans-serif; +} diff --git a/awx/templates/error.html b/awx/templates/error.html index 815235ebfc85..8f6dcc754aeb 100644 --- a/awx/templates/error.html +++ b/awx/templates/error.html @@ -18,7 +18,7 @@ @@ -74,5 +81,14 @@ {{ block.super }} + +{% is_proxied_request as proxied %} +{% if proxied %} + +{% else %} +{% endif %} {% endblock %} diff --git a/awx/templates/rest_framework/login.html b/awx/templates/rest_framework/login.html index 343d8a6ce0bb..00031dcc8ff8 100644 --- a/awx/templates/rest_framework/login.html +++ b/awx/templates/rest_framework/login.html @@ -9,7 +9,7 @@
-
+ {% csrf_token %}
{% if form.username.errors %}

{{ form.username.errors|striptags }}

@@ -31,7 +31,8 @@
+ autocorrect="off" class="form-control textinput textInput" id="id_password" + autocomplete="off" required> {% if form.password.errors %}

{{ form.password.errors|striptags }}

{% endif %} diff --git a/awx/ui/.babel.rc b/awx/ui/.babel.rc deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/awx/ui/.eslintignore b/awx/ui/.eslintignore deleted file mode 100644 index bc21e465196d..000000000000 --- a/awx/ui/.eslintignore +++ /dev/null @@ -1,11 +0,0 @@ -jest.*.js -webpack.*.js - -etc -coverage -build -node_modules -dist -images -instrumented -*test*.js diff --git a/awx/ui/.eslintrc.json b/awx/ui/.eslintrc.json deleted file mode 100644 index 7cf4965cbd80..000000000000 --- a/awx/ui/.eslintrc.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "parser": "@babel/eslint-parser", - "ignorePatterns": ["./node_modules/"], - "parserOptions": { - "requireConfigFile": false, - "ecmaVersion": 6, - "sourceType": "module", - "ecmaFeatures": { - "jsx": true, - "modules": true - }, - "babelOptions": { - "presets": ["@babel/preset-react"] - } - }, - "plugins": ["react-hooks", "jsx-a11y", "i18next", "@babel"], - "extends": [ - "airbnb", - "prettier", - "plugin:jsx-a11y/strict", - "plugin:i18next/recommended" - ], - "settings": { - "react": { - "version": "detect" - }, - "import/resolver": { - "node": { - "paths": ["src"] - } - } - }, - "env": { - "browser": true, - "node": true, - "jest": true - }, - "globals": { - "window": true - }, - "rules": { - "i18next/no-literal-string": [ - 2, - { - "markupOnly": true, - "ignoreAttribute": [ - "dateFieldName", - "timeFieldName", - "to", - "streamType", - "path", - "component", - "variant", - "key", - "position", - "promptName", - "color", - "promptId", - "headingLevel", - "size", - "target", - "autoComplete", - "trigger", - "from", - "name", - "fieldId", - "css", - "gutter", - "dataCy", - "tooltipMaxWidth", - "mode", - "aria-labelledby", - "aria-hidden", - "aria-controls", - "aria-pressed", - "sortKey", - "ouiaId", - "credentialTypeNamespace", - "link", - "value", - "credentialTypeKind", - "linkTo", - "scrollToAlignment", - "displayKey", - "sortedColumnKey", - "maxHeight", - "role", - "aria-haspopup", - "dropDirection", - "resizeOrientation", - "src", - "theme", - "gridColumns", - "rows", - "href", - "modifier", - "data-cy", - "fieldName", - "splitButtonVariant", - "pageKey" - ], - "ignore": [ - "Ansible", - "Tower", - "JSON", - "YAML", - "lg", - "hh:mm AM/PM", - "Twilio" - ], - "ignoreComponent": [ - "AboutModal", - "code", - "Omit", - "PotentialLink", - "TypeRedirect", - "Radio", - "RunOnRadio", - "NodeTypeLetter", - "SelectableItem", - "Dash", - "Plural" - ], - "ignoreCallee": ["describe"] - } - ], - "camelcase": "off", - "arrow-parens": "off", - "comma-dangle": "off", - // https://github.com/benmosher/eslint-plugin-import/issues/479#issuecomment-252500896 - "import/no-extraneous-dependencies": "off", - "max-len": [ - "error", - { - "code": 100, - "ignoreStrings": true, - "ignoreTemplateLiterals": true - } - ], - "no-continue": "off", - "no-debugger": "off", - "no-mixed-operators": "off", - "no-param-reassign": "off", - "no-plusplus": "off", - "no-underscore-dangle": "off", - "no-use-before-define": "off", - "no-multiple-empty-lines": ["error", { "max": 1 }], - "object-curly-newline": "off", - "no-trailing-spaces": ["error"], - "no-unused-expressions": ["error", { "allowShortCircuit": true }], - "react/jsx-props-no-spreading": ["off"], - "react/prefer-stateless-function": "off", - "react/prop-types": "off", - "react/sort-comp": ["error", {}], - "jsx-a11y/label-has-for": "off", - "jsx-a11y/label-has-associated-control": "off", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", - "react/jsx-filename-extension": "off", - "no-restricted-exports": "off", - "react/function-component-definition": "off", - "prefer-regex-literals": "off" - } -} diff --git a/awx/ui/.linguirc b/awx/ui/.linguirc deleted file mode 100644 index 9d8897f81ecf..000000000000 --- a/awx/ui/.linguirc +++ /dev/null @@ -1,17 +0,0 @@ -{"catalogs":[{ - "path": "/locales/{locale}/messages", - "include": [""], - "exclude": ["**/node_modules/**"] -}], -"compileNamespace": "cjs", -"extractBabelOptions": {}, -"compilerBabelOptions": {}, -"fallbackLocales": { "default": "en"}, -"format": "po", -"locales": ["en","es","fr","ko","nl","zh","ja","zu"], -"orderBy": "messageId", -"pseudoLocale": "zu", -"rootDir": "./src", -"runtimeConfigModule": ["@lingui/core", "i18n"], -"sourceLocale": "en" -} diff --git a/awx/ui/.npmrc b/awx/ui/.npmrc deleted file mode 100644 index c42da845b449..000000000000 --- a/awx/ui/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict = true diff --git a/awx/ui/.prettierignore b/awx/ui/.prettierignore deleted file mode 100644 index 692588cdabc6..000000000000 --- a/awx/ui/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -build -src/locales diff --git a/awx/ui/.prettierrc b/awx/ui/.prettierrc deleted file mode 100644 index 93b2f46c80a0..000000000000 --- a/awx/ui/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "printWidth": 80, - "tabWidth": 2, - "semi": true, - "singleQuote": true, - "trailingComma": "es5", - "bracketSpacing": true -} diff --git a/awx/ui/CONTRIBUTING.md b/awx/ui/CONTRIBUTING.md deleted file mode 100644 index dba8b13423ef..000000000000 --- a/awx/ui/CONTRIBUTING.md +++ /dev/null @@ -1,363 +0,0 @@ -# Ansible AWX UI With PatternFly - -Hi there! We're excited to have you as a contributor. - -Have questions about this document or anything not covered here? Feel free to reach out to any of the contributors of this repository. - -## Table of contents - -- [Ansible AWX UI With PatternFly](#ansible-awx-ui-with-patternfly) - - [Table of contents](#table-of-contents) - - [Things to know prior to submitting code](#things-to-know-prior-to-submitting-code) - - [Setting up your development environment](#setting-up-your-development-environment) - - [Prerequisites](#prerequisites) - - [Node and npm](#node-and-npm) - - [Build the User Interface](#build-the-user-interface) - - [Accessing the AWX web interface](#accessing-the-awx-web-interface) - - [AWX REST API Interaction](#awx-rest-api-interaction) - - [Handling API Errors](#handling-api-errors) - - [Forms](#forms) - - [Working with React](#working-with-react) - - [App structure](#app-structure) - - [Patterns](#patterns) - - [Bootstrapping the application (root src/ files)](#bootstrapping-the-application-root-src-files) - - [Naming files](#naming-files) - - [Naming components that use the context api](#naming-components-that-use-the-context-api) - - [Class constructors vs Class properties](#class-constructors-vs-class-properties) - - [Binding](#binding) - - [Typechecking with PropTypes](#typechecking-with-proptypes) - - [Custom Hooks](#custom-hooks) - - [Naming Functions](#naming-functions) - - [Default State Initialization](#default-state-initialization) - - [Testing components that use contexts](#testing-components-that-use-contexts) - - [Internationalization](#internationalization) - - [Marking strings for translation and replacement in the UI](#marking-strings-for-translation-and-replacement-in-the-ui) - - [Setting up .po files to give to translation team](#setting-up-po-files-to-give-to-translation-team) - - [Marking an issue to be translated](#marking-an-issue-to-be-translated) - -## Things to know prior to submitting code - -- All code submissions are done through pull requests against the `devel` branch. -- If collaborating with someone else on the same branch, please use `--force-with-lease` instead of `--force` when pushing up code. This will prevent you from accidentally overwriting commits pushed by someone else. For more information, see https://git-scm.com/docs/git-push#git-push---force-with-leaseltrefnamegt -- We use a [code formatter](https://prettier.io/). Before adding a new commit or opening a PR, please apply the formatter using `npm run prettier` -- We adopt the following code style guide: - - functions should adopt camelCase - - constructors/classes should adopt PascalCase - - constants to be exported should adopt UPPERCASE -- For strings, we adopt the `sentence capitalization` since it is a [Patternfly style guide](https://www.patternfly.org/v4/ux-writing/capitalization). - -## Setting up your development environment - -The UI is built using [ReactJS](https://reactjs.org/docs/getting-started.html) and [Patternfly](https://www.patternfly.org/). - -### Prerequisites - -#### Node and npm - -The AWX UI requires the following: - -- Node >= 16.13.1 LTS -- NPM 8.x - -Run the following to install all the dependencies: - -```bash -(host) $ npm install -``` - -#### Build the User Interface - -Run the following to build the AWX UI: - -```bash -(host) $ npm run start -``` - -## Accessing the AWX web interface - -You can now log into the AWX web interface at [https://127.0.0.1:3001](https://127.0.0.1:3001). - -## AWX REST API Interaction - -This interface is built on top of the AWX REST API. If a component needs to interact with the API then the model that corresponds to that base endpoint will need to be imported from the api module. - -Example: - -`import { OrganizationsAPI, UsersAPI } from '../../../api';` - -All models extend a `Base` class which provides an interface to the standard HTTP methods (GET, POST, PUT etc). Methods that are specific to that endpoint should be added directly to model's class. - -**Mixins** - For related endpoints that apply to several different models a mixin should be used. Mixins are classes with a number of methods and can be used to avoid adding the same methods to a number of different models. A good example of this is the Notifications mixin. This mixin provides generic methods for reading notification templates and toggling them on and off. -Note that mixins can be chained. See the example below. - -Example of a model using multiple mixins: - -```javascript -import NotificationsMixin from '../mixins/Notifications.mixin'; -import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; - -class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) { - ... -} - -export default Organizations; -``` - -**Testing** - The easiest way to mock the api module in tests is to use jest's [automatic mock](https://jestjs.io/docs/en/es6-class-mocks#automatic-mock). This syntax will replace the class with a mock constructor and mock out all methods to return undefined by default. If necessary, you can still override these mocks for specific tests. See the example below. - -Example of mocking a specific method for every test in a suite: - -```javascript -import { OrganizationsAPI } from '../../../../src/api'; - -// Mocks out all available methods. Comparable to: -// OrganizationsAPI.readAccessList = jest.fn(); -// but for every available method -jest.mock('../../../../src/api'); - -// Return a specific mock value for the readAccessList method -beforeEach(() => { - OrganizationsAPI.readAccessList.mockReturnValue({ foo: 'bar' }); -}); - -// Reset mocks -afterEach(() => { - jest.clearAllMocks(); -}); - -... -``` - -**Test Attributes** - -It should be noted that the `dataCy` prop, as well as its equivalent attribute `data-cy`, are used as flags for any UI test that wants to avoid relying on brittle CSS selectors such as `nth-of-type()`. - -## Handling API Errors - -API requests can and will fail occasionally so they should include explicit error handling. The three _main_ categories of errors from our perspective are: content loading errors, form submission errors, and other errors. The patterns currently in place for these are described below: - -- **content loading errors** - These are any errors that occur when fetching data to initialize a page or populate a list. For these, we conditionally render a _content error component_ in place of the unresolved content. - -- **form submission errors** - If an error is encountered when submitting a form, we display the error message on the form. For field-specific validation errors, we display the error message beneath the specific field(s). For general errors, we display the error message at the bottom of the form near the action buttons. An error that happens when requesting data to populate a form is not a form submission error, it is still a content error and is handled as such (see above). - -- **other errors** - Most errors will fall into the first two categories, but for miscellaneous actions like toggling notifications, deleting a list item, etc. we display an alert modal to notify the user that their requested action couldn't be performed. - -## Forms - -Our forms should have a known, consistent, and fully-resolved starting state before it is possible for a user, keyboard-mouse, screen reader, or automated test to interact with them. If multiple network calls are needed to populate a form, resolve them all before displaying the form or showing a content error. When multiple requests are needed to create or update the resources represented by a form, resolve them all before transitioning the ui to a success or failure state. - -## Working with React - -### App structure - -All source code lives in the `/src` directory and all tests are colocated with the components that they test. - -Inside these folders, the internal structure is: - -- **/api** - All classes used to interact with API's are found here. See [AWX REST API Interaction](#awx-rest-api-interaction) for more information. -- **/components** - All generic components that are meant to be used in multiple contexts throughout awx. Things like buttons, tabs go here. -- **/contexts** - Components which utilize react's context api. -- **/hooks** - Custom react [hooks](https://reactjs.org/docs/hooks-custom.html) -- **/locales** - [Internationalization](#internationalization) config and source files. -- **/screens** - Based on the various routes of awx. - - **/shared** - Components that are meant to be used specifically by a particular route, but might be sharable across pages of that route. For example, a form component which is used on both add and edit screens. -- **/util** - Stateless helper functions that aren't tied to react. - -### Patterns - -- A **screen** shouldn't import from another screen. If a component _needs_ to be shared between two or more screens, it is a generic and should be moved to `src/components`. - -#### Bootstrapping the application (root src/ files) - -In the root of `/src`, there are a few files which are used to initialize the react app. These are - -- **index.js** - - Connects react app to root dom node. - - Sets up root route structure, navigation grouping and login modal - - Calls base context providers - - Imports .scss styles. -- **app.js** - - Sets standard page layout, about modal, and root dialog modal. -- **RootProvider.js** - - Sets up all context providers. - - Initializes i18n and router - -### Naming files - -Ideally, files should be named the same as the component they export, and tests with `.test` appended. In other words, `` would be defined in `FooBar.js`, and its tests would be defined in `FooBar.test.js`. - -#### Naming components that use the context api - -**File naming** - Since contexts export both consumer and provider (and potentially in withContext function form), the file can be simplified to be named after the consumer export. In other words, the file containing the `Network` context components would be named `Network.js`. - -**Component naming and conventions** - In order to provide a consistent interface with react-router and [lingui](https://lingui.js.org/), as well as make their usage easier and less verbose, context components follow these conventions: - -- Providers are wrapped in a component in the `FooProvider` format. - - The value prop of the provider should be pulled from state. This is recommended by the react docs, [here](https://reactjs.org/docs/context.html#caveats). - - The provider should also be able to accept its value by prop for testing. - - Any sort of code related to grabbing data to put on the context should be done in this component. -- Consumers are wrapped in a component in the `Foo` format. -- If it makes sense, consumers can be exported as a function in the `withFoo()` format. If a component is wrapped in this function, its context values are available on the component as props. - -### Class constructors vs Class properties - -It is good practice to use constructor-bound instance methods rather than methods as class properties. Methods as arrow functions provide lexical scope and are bound to the Component class instance instead of the class itself. This makes it so we cannot easily test a Component's methods without invoking an instance of the Component and calling the method directly within our tests. - -BAD: - -```javascript -class MyComponent extends React.Component { - constructor(props) { - super(props); - } - - myEventHandler = () => { - // do a thing - }; -} -``` - -GOOD: - -```javascript -class MyComponent extends React.Component { - constructor(props) { - super(props); - this.myEventHandler = this.myEventHandler.bind(this); - } - - myEventHandler() { - // do a thing - } -} -``` - -### Binding - -It is good practice to bind our class methods within our class constructor method for the following reasons: - -1. Avoid defining the method every time `render()` is called. -2. [Performance advantages](https://stackoverflow.com/a/44844916). -3. Ease of [testing](https://github.com/airbnb/enzyme/issues/365). - -### Typechecking with PropTypes - -Shared components should have their prop values typechecked. This will help catch bugs when components get refactored/renamed. - -```javascript -About.propTypes = { - ansible_version: PropTypes.string, - isOpen: PropTypes.bool, - onClose: PropTypes.func.isRequired, - version: PropTypes.string, -}; - -About.defaultProps = { - ansible_version: null, - isOpen: false, - version: null, -}; -``` - -### Custom Hooks - -There are currently a few custom hooks: - -1. [useRequest](https://github.com/ansible/awx/blob/devel/awx/ui/src/util/useRequest.js#L21) encapsulates main actions related to requests. -2. [useDismissableError](https://github.com/ansible/awx/blob/devel/awx/ui/src/util/useRequest.js#L71) provides controls for "dismissing" an error message. -3. [useDeleteItems](https://github.com/ansible/awx/blob/devel/awx/ui/src/util/useRequest.js#L98) handles deletion of items from a paginated item list. -4. [useSelected](https://github.com/ansible/awx/blob/devel/awx/ui/src/util/useSelected.js#L14) provides a way to read and update a selected list. - -### Naming Functions - -Here are the guidelines for how to name functions. - -| Naming Convention | Description | -| ----------------- | --------------------------------------------------------------------------------- | -| `handle` | Use for methods that process events | -| `on` | Use for component prop names | -| `toggle` | Use for methods that flip one value to the opposite value | -| `show` | Use for methods that always set a value to show or add an element | -| `hide` | Use for methods that always set a value to hide or remove an element | -| `create` | Use for methods that make API `POST` requests | -| `read` | Use for methods that make API `GET` requests | -| `update` | Use for methods that make API `PATCH` requests | -| `destroy` | Use for methods that make API `DESTROY` requests | -| `replace` | Use for methods that make API `PUT` requests | -| `disassociate` | Use for methods that pass `{ disassociate: true }` as a data param to an endpoint | -| `associate` | Use for methods that pass a resource id as a data param to an endpoint | -| `can` | Use for props dealing with RBAC to denote whether a user has access to something | - -### Default State Initialization - -When declaring empty initial states, prefer the following instead of leaving them undefined: - -```javascript -this.state = { - somethingA: null, - somethingB: [], - somethingC: 0, - somethingD: {}, - somethingE: '', -}; -``` - -### Testing components that use contexts - -We have several React contexts that wrap much of the app, including those from react-router, lingui, and some of our own. When testing a component that depends on one or more of these, you can use the `mountWithContexts()` helper function found in `testUtils/enzymeHelpers.js`. This can be used just like Enzyme's `mount()` function, except it will wrap the component tree with the necessary context providers and basic stub data. - -If you want to stub the value of a context, or assert actions taken on it, you can customize a contexts value by passing a second parameter to `mountWithContexts`. For example, this provides a custom value for the `Config` context: - -```javascript -const config = { - custom_virtualenvs: ['foo', 'bar'], -}; -mountWithContexts(, { - context: { config }, -}); -``` - -Now that these custom virtual environments are available in this `OrganizationForm` test we can assert that the component that displays -them is rendering properly. - -The object containing context values looks for five known contexts, identified by the keys `linguiPublisher`, `router`, `config`, `network`, and `dialog` — the latter three each referring to the contexts defined in `src/contexts`. You can pass `false` for any of these values, and the corresponding context will be omitted from your test. For example, this will mount your component without the dialog context: - -```javascript -mountWithContexts(< { - context: { - dialog: false, - } -}); -``` - -## Internationalization - -Internationalization leans on the [lingui](https://github.com/lingui/js-lingui) project. [Official documentation here](https://lingui.js.org/). We use this library to mark our strings for translation. If you want to see this in action you'll need to take the following steps: - -### Marking strings for translation and replacement in the UI - -The lingui library provides various React helpers for dealing with both marking strings for translation, and replacing strings that have been translated. For consistency and ease of use, we have consolidated on one pattern for the codebase. To set strings to be translated in the UI: - -- import the t template tag function from the @lingui/macro package. -- wrap your string using the following format: `` t`String to be translated` `` - -**Note:** If you have a variable string with text that needs translating, you must wrap it in `` t`${variable} string` `` where it is defined. Then you must run `npm run extract-strings` to generate new `.po` files and submit those files along with your pull request. - -**Note:** We try to avoid the `I18n` consumer, or `i18nMark` function lingui gives us access to in this repo. i18nMark does not actually replace the string in the UI (leading to the potential for untranslated bugs), and the other helpers are redundant. Settling on a consistent, single pattern helps us ease the mental overhead of the need to understand the ins and outs of the lingui API. - -**Note:** Pluralization can be complicated so it is best to allow lingui handle cases where we have a string that may need to be pluralized based on number of items, or count. In that case lingui provides a `` component, and a `plural()` function. When adding or updating strings in a `` tag you must run `npm run extra-strings` and submit the new `.po` files with your pull request. See documentation [here](https://lingui.js.org/guides/plurals.html?highlight=pluralization). - -You can learn more about the ways lingui and its React helpers at [this link](https://lingui.js.org/tutorials/react-patterns.html). - -### Setting up .po files to give to translation team - -1. Make sure that the languages you intend to translate are set correctly in the `.linguirc` configuration file. -2. `npm run extract-strings` to create .po files for each language specified. The .po files will be placed in src/locales. When updating strings that are used by `` or `plural()` you will need to run this command to get the strings to render properly. This command will create `.po` files for each of the supported languages that will need to be committed with your PR. -3. Open up the .po file for the language you want to test and add some translations. In production we would pass this .po file off to the translation team. -4. Once you've edited your .po file (or we've gotten a .po file back from the translation team) run `npm run compile-strings`. This command takes the .po files and turns them into a minified JSON object and can be seen in the `messages.js` file in each locale directory. These files get loaded at the App root level (see: App.js). -5. Change the language in your browser and reload the page. You should see your specified translations in place of English strings. - -### Marking an issue to be translated - -1. Issues marked with `component:I10n` should not be closed after the issue was fixed. -2. Remove the label `state:needs_devel`. -3. Add the label `state:pending_translations`. At this point, the translations will be batch translated by a maintainer, creating relevant entries in the PO files. Then after those translations have been merged, the issue can be closed. diff --git a/awx/ui/Dockerfile b/awx/ui/Dockerfile deleted file mode 100644 index fda48b9c9276..000000000000 --- a/awx/ui/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM node:16.13.1 -ARG NPMRC_FILE=.npmrc -ENV NPMRC_FILE=${NPMRC_FILE} -ARG TARGET='https://awx:8043' -ENV TARGET=${TARGET} -ENV CI=true -WORKDIR /ui -ADD .eslintignore .eslintignore -ADD .eslintrc.json .eslintrc.json -ADD .linguirc .linguirc -ADD jsconfig.json jsconfig.json -ADD public public -ADD package.json package.json -ADD package-lock.json package-lock.json -COPY ${NPMRC_FILE} .npmrc -RUN npm install -ADD src src -EXPOSE 3001 -CMD [ "npm", "start" ] diff --git a/awx/ui/Makefile b/awx/ui/Makefile new file mode 100644 index 000000000000..53b498d20bc7 --- /dev/null +++ b/awx/ui/Makefile @@ -0,0 +1,123 @@ +## UI_DIR: Relative path to the directory containing this Makefile +UI_DIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) + +## Path to your local clone of the UI repo +# NOTE: you will not be able to build within the docker-compose development environment if you use this option +UI_LOCAL ?= + +## Git repo and branch to the UI repo +UI_GIT_REPO ?= https://github.com/ansible/ansible-ui.git +UI_GIT_BRANCH ?= main + +## Product name to display on the UI used in UI build process +PRODUCT ?= AWX + +.PHONY: ui +## Default build target of ui Makefile, builds ui/build +ui: ui/build + +.PHONY: ui/build +## Build ui/build +ui/build: $(UI_DIR)/build + +## True build target for ui. +$(UI_DIR)/build: + @$(MAKE) $(UI_DIR)/src/build/awx + @echo "=== Copying $(UI_DIR)/src/build to $(UI_DIR)/build ===" + @rm -rf $(UI_DIR)/build + @cp -r $(UI_DIR)/src/build $(UI_DIR) + @echo "=== Done building $(UI_DIR)/build ===" + +.PHONY: ui/src/build +## Build ui/src/build +ui/src/build: $(UI_DIR)/src/build/awx + +## True target for ui/src/build. Build ui from source. +$(UI_DIR)/src/build/awx: $(UI_DIR)/src $(UI_DIR)/src/node_modules/webpack + @echo "=== Building ui ===" + @cd $(UI_DIR)/src && PRODUCT="$(PRODUCT)" PUBLIC_PATH=/static/awx/ ROUTE_PREFIX=/ npm run build:awx + @mv $(UI_DIR)/src/build/awx/index.html $(UI_DIR)/src/build/awx/index_awx.html + +.PHONY: ui/src +## Clone or link src of UI to ui/src, will re-clone/link/update if necessary. +ui/src: $(UI_DIR)/src + +# TODO: Rewrite this big bash script in a more readable way. +## True target for ui/src. +$(UI_DIR)/src: + @echo "=== Setting up $(UI_DIR)/src ===" + @if [ ! -z "$(UI_LOCAL)" ]; then \ + if [ -d $(UI_DIR)/src ]; then \ + if [ "$$(readlink $(UI_DIR)/src)" = "$(UI_LOCAL)" ]; then \ + echo "SKIP: ui/src. $(UI_DIR)/src already linked to $(UI_LOCAL)."; \ + else \ + echo "=== Linking $(UI_DIR)/src to $(UI_LOCAL) ==="; \ + rm -rf $(UI_DIR)/src; \ + ln -s $(UI_LOCAL) $(UI_DIR)/src; \ + fi; \ + else \ + echo "=== Linking $(UI_DIR)/src to $(UI_LOCAL) ==="; \ + ln -s $(UI_LOCAL) $(UI_DIR)/src; \ + fi; \ + elif [ ! -z "$(UI_GIT_REPO)" ]; then \ + if [ -d $(UI_DIR)/src ]; then \ + GIT_REMOTE_ORIGIN=$$(cd $(UI_DIR)/src && git remote get-url origin); \ + GIT_REMOTE_BRANCH=$$(cd $(UI_DIR)/src && git rev-parse --abbrev-ref HEAD); \ + if [ "$$GIT_REMOTE_ORIGIN" = "$(UI_GIT_REPO)" ] && [ "$$GIT_REMOTE_BRANCH" = "$(UI_GIT_BRANCH)" ]; then \ + echo "=== Updating $(UI_DIR)/src from $(UI_GIT_BRANCH) of $(UI_GIT_REPO) ==="; \ + git fetch && git pull; \ + else \ + echo "=== Cloning $(UI_DIR)/src from $(UI_GIT_BRANCH) of $(UI_GIT_REPO) ==="; \ + rm -rf $(UI_DIR)/src; \ + git clone --depth 1 --branch $(UI_GIT_BRANCH) $(UI_GIT_REPO) $(UI_DIR)/src || true; \ + fi; \ + else \ + echo "=== Cloning $(UI_DIR)/src from $(UI_GIT_BRANCH) of $(UI_GIT_REPO) ==="; \ + git clone --depth 1 --branch $(UI_GIT_BRANCH) $(UI_GIT_REPO) $(UI_DIR)/src || true; \ + fi; \ + else \ + echo "FAILED: ui/src. UI_LOCAL and UI_GIT_REPO are not set."; \ + exit 1; \ + fi + +.PHONY: ui/src/webpack +## Install webpack. +ui/src/webpack: $(UI_DIR)/src/node_modules/webpack + +## True target for ui/src/webpack. +$(UI_DIR)/src/node_modules/webpack: + @echo "=== Installing webpack ===" + @cd $(UI_DIR)/src && \ + maj=$$(node -p "process.versions.node.split('.')[0]"); \ + if [ "$$maj" != "18" ]; then \ + echo "Error: Need Node 18.x; found $$(node -v)" >&2; \ + exit 1; \ + fi; \ + npm install webpack + + +.PHONY: clean/ui +## Clean ui +clean/ui: clean/ui/build clean/ui/src + +.PHONY: clean/ui/src +## Clean ui src +clean/ui/src: + rm -rf $(UI_DIR)/src + +.PHONY: clean/ui/build +## Clean ui build +clean/ui/build: + rm -rf $(UI_DIR)/build + +.PHONY: $(UI_DIR)/clean +## Alias for clean/ui, so we can run `make clean` directly in ui +$(UI_DIR)/clean: clean/ui + +.PHONY: $(UI_DIR)/clean/src +## Alias for clean/ui/src, so we can run `make clean/src` directly in ui +$(UI_DIR)/clean/src: clean/ui/src + +.PHONY: $(UI_DIR)/clean/build +## Alias for clean/ui/build, so we can run `make clean/build` directly in ui +$(UI_DIR)/clean/build: clean/ui/build diff --git a/awx/ui/README.md b/awx/ui/README.md index 4c7bb9d58025..2b0cf61329c4 100644 --- a/awx/ui/README.md +++ b/awx/ui/README.md @@ -1,115 +1,47 @@ -# AWX-UI +# Instruction to build ui directly from this directory -## Requirements -- node >= 16.13.1, npm >= 8.x make, git +## Set src of the ui repo -## Development -The API development server will need to be running. See [CONTRIBUTING.md](../../CONTRIBUTING.md). +### Via GIT -```shell -# install -npm --prefix=awx/ui install - -# Start the ui development server. While running, the ui will be reachable -# at https://127.0.0.1:3001 and updated automatically when code changes. -npm --prefix=awx/ui start +```bash +export UI_GIT_REPO=https:// ``` -### Build for the Development Containers -If you just want to build a ui for the container-based awx development -environment and do not need to work on the ui code, use these make targets: - -```shell -# The ui will be reachable at https://localhost:8043 or -# http://localhost:8013 -make ui-devel +or -# clean up -make clean-ui +```bash +export UI_GIT_REPO=git@ ``` -### Using an External Server -If you normally run awx on an external host/server (in this example, `awx.local`), -you'll need use the `TARGET` environment variable when starting the ui development -server: +optionally set branch (default is main) -```shell -TARGET='https://awx.local:8043' npm --prefix awx/ui start +```bash +export UI_GIT_BRANCH=main ``` -## Testing -```shell -# run code formatting check -npm --prefix awx/ui run prettier-check - -# run lint checks -npm --prefix awx/ui run lint - -# run all unit tests -npm --prefix awx/ui run test +### Via symlink to existing clone -# run a single test (in this case the login page test): -npm --prefix awx/ui test -- src/screens/Login/Login.test.jsx +NOTE: UI_LOCAL have higher precedence than UI_GIT_REPO, if UI_LOCAL is set, UI_GIT_REPO will be ignored. -# start the test watcher and run tests on files that you've changed -npm --prefix awx/ui run test-watch - -# start the tests and get the coverage report after the tests have completed -npm --prefix awx/ui run test -- --coverage +```bash +export UI_LOCAL = /path/to/your/ui ``` -#### Note: -- Once the test watcher is up and running you can hit `a` to run all the tests. -- All commands are run on your host machine and not in the api development containers. - - -## Updating Dependencies -It is not uncommon to run the ui development tooling outside of the awx development -container. That said, dependencies should always be modified from within the -container to ensure consistency. - -```shell -# make sure the awx development container is running and open a shell -docker exec -it tools_awx_1 bash -# start with a fresh install of the current dependencies -(tools_awx_1)$ make clean-ui && npm --prefix=awx/ui ci +## Build -# add an exact development dependency -(tools_awx_1)$ npm --prefix awx/ui install --save-dev --save-exact dev-package@1.2.3 - -# add an exact production dependency -(tools_awx_1)$ npm --prefix awx/ui install --save --save-exact prod-package@1.23 - -# remove a development dependency -(tools_awx_1)$ npm --prefix awx/ui uninstall --save-dev dev-package - -# remove a production dependency -(tools_awx_1)$ npm --prefix awx/ui uninstall --save prod-package +```bash +make ui +``` -# exit the container -(tools_awx_1)$ exit +## Rebuild -# add the updated package.json and package-lock.json files to scm -git add awx/ui/package.json awx/ui/package-lock.json +```bash +make -B ui ``` -#### Note: -- Building the ui can use up a lot of resources. If you're running docker for mac or similar -virtualization, the default memory limit may not be enough and you should increase it. - -## Building for Production -```shell -# built files are placed in awx/ui/build -npm --prefix awx/ui run build -``` - -## CI Container -To run: +## Clean -```shell -cd awx/awx/ui -docker build -t awx-ui . -docker run --name tools_ui_1 --network _sources_default --link 'tools_awx_1:awx' -e TARGET="https://awx:8043" -p '3001:3001' --rm -v $(pwd)/src:/ui/src awx-ui +```bash +make clean/ui ``` - -**Note:** This is for CI, test systems, zuul, etc. For local development, see [usage](https://github.com/ansible/awx/blob/devel/awx/ui/README.md#Development) diff --git a/awx/ui/SEARCH.md b/awx/ui/SEARCH.md deleted file mode 100644 index 4893b5ba131b..000000000000 --- a/awx/ui/SEARCH.md +++ /dev/null @@ -1,416 +0,0 @@ -# Simple Search - -## UX Considerations - -Historically, the code that powers search in the AngularJS version of the AWX UI is very complex and prone to bugs. In order to reduce that complexity, we've made some UX decisions to help make the code easier to maintain. - -**ALL query params namespaced and in url bar** - -This includes lists that aren't necessarily hyperlinked, like lookup lists. The reason behind this is so we can treat the url bar as the source of truth for queries always. Any params that have both a key AND value that is in the defaultParams section of the qs config are stripped out of the search string (see "Encoding for UI vs. API" for more info on this point) - -**Django fuzzy search (`?search=`) is not accessible outside of "advanced search"** - -In current smart search typing a term with no key utilizes `?search=` i.e. for "foo" tag, `?search=foo` is given. `?search=` looks on a static list of field name "guesses" (such as name, description, etc.), as well as specific fields as defined for each endpoint (for example, the events endpoint looks for a "stdout" field as well). Due to the fact a key will always be present on the left-hand of simple search, it doesn't make sense to use `?search=` as the default. - -We may allow passing of `?search=` through our future advanced search interface. Some details that were gathered in planning phases about `?search=` that might be helpful in the future: - -- `?search=` tags are OR'd together (union is returned). -- `?search=foo&name=bar` returns items that have a name field of bar (not case insensitive) AND some text field with foo on it -- `?search=foo&search=bar&name=baz` returns (foo in name OR foo in description OR ...) AND (bar in name OR bar in description OR ...) AND (baz in name) -- similarly `?related__search=` looks on the static list of "guesses" for models related to the endpoint. The specific fields are not "searched" for `?related__search=`. -- `?related__search=` not currently used in awx ui - -**A note on clicking a tag to putting it back into the search bar** - -This was brought up as a nice to have when we were discussing our initial implementation of search in the new application. Since there isn't a way we would be able to know if the user created the tag from the simple or advanced search interface, we wouldn't know where to put it back. This breaks our idea of using the query params as the exclusive source of truth, so we've decided against implementing it for now. - -## Tasklist - -### DONE - -- DONE update handleSearch to follow handleSort param -- DONE update qsConfig columns to utilize isSearchable bool (just like isSortable bool) -- DONE enter keydown in text search bar to search -- DONE get decoded params and write test -- DONE make list header component -- DONE make filter component -- DONE make filters show up for empty list -- DONE make clear all button -- DONE styling of FilterTags component -- DONE clear out text input after tag has been made -- DONE deal with duplicate key tags being added/removed in qs util file -- DONE deal with widgetry changing between one dropdown option to the left of search and many -- DONE bug: figure out why ?name=org returning just org not “org 2” -- DONE update contrib file to have the first section with updated text as is in this pr description. -- DONE rebase with latest awx-pf changes -- DONE styling of search bar -- DONE make filter and list header tests -- DONE change api paramsSerializer to handle duplicate key stuff -- DONE update qs update function to be smaller, simple param functions, as opposed to one big one with a lot of params -- DONE add search filter removal test for qs. -- DONE remove button for search tags of duplicate keys are broken, fix that - -### TODO pre-holiday break - -- Update COLUMNS to SORT_COLUMNS and SEARCH_COLUMNS -- Update to using new PF Toolbar component (currently an experimental component) -- Change the right-hand input based on the type of key selected on the left-hand side. In addition to text input, for our MVP we will support: - - number input - - select input (multiple-choice configured from UI or Options) -- Update the following lists to have the following keys: - -**Jobs list** (signed off earlier in chat) - -- Name (which is also the name of the job template) - search is ?name=jt -- Job ID - search is ?id=13 -- Label name - search is ?labels\_\_name=foo -- Job type (dropdown on right with the different types) ?type = job -- Created by (username) - search is ?created_by\_\_username=admin -- Status - search (dropdown on right with different statuses) is ?status=successful - -Instances of jobs list include: - -- Jobs list -- Host completed jobs list -- JT completed jobs list - -**Organization list** - -- Name - search is ?name=org -- ? Team name (of a team in the org) - search is ?teams\_\_name=ansible -- ? Username (of a user in the org) - search is ?users\_\_username=johndoe - -Instances of orgs list include: - -- Orgs list -- User orgs list -- Lookup on Project -- Lookup on Credential -- Lookup on Inventory -- User access add wizard list -- Team access add wizard list - -**Instance Groups list** - -- Name - search is ?name=ig -- ? is_container_group boolean choice (doesn't work right now in API but will soon) - search is ?is_container_group=true -- ? credential name - search is ?credentials\_\_name=kubey - -Instance of instance groups list include: - -- Lookup on Org -- Lookup on JT -- Lookup on Inventory - -**Users list** - -- Username - search is ?username=johndoe -- First Name - search is ?first_name=John -- Last Name - search is ?last_name=Doe -- ? (if not superfluous, would not include on Team users list) Team Name - search is ?teams\_\_name=team_of_john_does (note API issue: User has no field named "teams") -- ? (only for access or permissions list) Role Name - search is ?roles\_\_name=Admin (note API issue: Role has no field "name") -- ? (if not superfluous, would not include on Organization users list) ORg Name - search is ?organizations\_\_name=org_of_jhn_does - -Instance of user lists include: - -- User list -- Org user list -- Access list for Org, JT, Project, Credential, Inventory, User and Team -- Access list for JT -- Access list Project -- Access list for Credential -- Access list for Inventory -- Access list for User -- Access list for Team -- Team add users list -- Users list in access wizard (to add new roles for a particular list) for Org -- Users list in access wizard (to add new roles for a particular list) for JT -- Users list in access wizard (to add new roles for a particular list) for Project -- Users list in access wizard (to add new roles for a particular list) for Credential -- Users list in access wizard (to add new roles for a particular list) for Inventory - -**Teams list** - -- Name - search is ?name=teamname -- ? Username (of a user in the team) - search is ?users\_\_username=johndoe -- ? (if not superfluous, would not include on Organizations teams list) Org Name - search is ?organizations\_\_name=org_of_john_does - -Instance of team lists include: - -- Team list -- Org team list -- User team list -- Team list in access wizard (to add new roles for a particular list) for Org -- Team list in access wizard (to add new roles for a particular list) for JT -- Team list in access wizard (to add new roles for a particular list) for Project -- Team list in access wizard (to add new roles for a particular list) for Credential -- Team list in access wizard (to add new roles for a particular list) for Inventory - -**Credentials list** - -- Name -- ? Type (dropdown on right with different types) -- ? Created by (username) -- ? Modified by (username) - -Instance of credential lists include: - -- Credential list -- Lookup for JT -- Lookup for Project -- User access add wizard list -- Team access add wizard list - -**Projects list** - -- Name - search is ?name=proj -- ? Type (dropdown on right with different types) - search is scm_type=git -- ? SCM URL - search is ?scm_url=github.com/ansible/test-playbooks -- ? Created by (username) - search is ?created_by\_\_username=admin -- ? Modified by (username) - search is ?modified_by\_\_username=admin - -Instance of project lists include: - -- Project list -- Lookup for JT -- User access add wizard list -- Team access add wizard list - -**Templates list** - -- Name - search is ?name=cleanup -- ? Type (dropdown on right with different types) - search is ?type=playbook_run -- ? Playbook name - search is ?job_template\_\_playbook=debug.yml -- ? Created by (username) - search is ?created_by\_\_username=admin -- ? Modified by (username) - search is ?modified_by\_\_username=admin - -Instance of template lists include: - -- Template list -- Project Templates list - -**Inventories list** - -- Name - search is ?name=inv -- ? Created by (username) - search is ?created_by\_\_username=admin -- ? Modified by (username) - search is ?modified_by\_\_username=admin - -Instance of inventory lists include: - -- Inventory list -- Lookup for JT -- User access add wizard list -- Team access add wizard list - -**Groups list** - -- Name - search is ?name=group_name -- ? Created by (username) - search is ?created_by\_\_username=admin -- ? Modified by (username) - search is ?modified_by\_\_username=admin - -Instance of group lists include: - -- Group list - -**Hosts list** - -- Name - search is ?name=hostname -- ? Created by (username) - search is ?created_by\_\_username=admin -- ? Modified by (username) - search is ?modified_by\_\_username=admin - -Instance of host lists include: - -- Host list - -**Notifications list** - -- Name - search is ?name=notification_template_name -- ? Type (dropdown on right with different types) - search is ?type=slack -- ? Created by (username) - search is ?created_by\_\_username=admin -- ? Modified by (username) - search is ?modified_by\_\_username=admin - -Instance of notification lists include: - -- Org notification list -- JT notification list -- Project notification list - -### TODO backlog - -- Change the right-hand input based on the type of key selected on the left-hand side. We will eventually want to support: - - lookup input (selection of particular resources, based on API list endpoints) - - date picker input -- Update the following lists to have the following keys: - - Update all **name and **username related field search-based keys to be type-ahead lookup based searches - -## Code Details - -### Search component - -The component looks like this: - -``` - -``` - -**qsConfig** is used to get namespace so that multiple lists can be on the page. When tags are modified they append namespace to query params. The qsConfig is also used to get "type" of fields in order to correctly parse values as int or date as it is translating. - -**columns** are passed as an array, as defined in the screen where the list is located. You pass a bool `isDefault` to indicate that should be the key that shows up in the left-hand dropdown as default in the UI. If you don't pass any columns, a default of `isDefault=true` will be added to a name column, which is nearly universally shared throughout the models of awx. - -There is a type attribute that can be `'string'`, `'number'` or `'choice'` (and in the future, `'date'` and `'lookup'`), which will change the type of input on the right-hand side of the search bar. For a key that has a set number of choices, you will pass a choices attribute, which is an array in the format choices: [{label: 'Foo', value: 'foo'}] - -**onSearch** calls the `mergeParams` qs util in order to add new tags to the queryset. mergeParams is used so that we can support duplicate keys (see mergeParams vs. replaceParams for more info). - -### ListHeader component - -`DataListToolbar`, `EmptyListControls`, and `FilterTags` components were created or moved to a new sub-component of `PaginatedDataList`, `ListHeader`. This allowed us to consolidate the logic between both lists with data (which need to show search, sort, any search tags currently active, and actions) as well as empty lists (which need to show search tags currently active so they can be removed, potentially getting you back to a "list-has-data" state, as well as a subset of options still valid, such as "add"). - -The ability to search and remove filters, as well as sort the list is handled through callbacks which are passed from functions defined in `ListHeader`. These are the following: - -- `handleSort(key, direction)` - use key and direction of sort to change the order_by value in the queryset -- `handleSearch(key, value)` - use key and value to push a new value to the param -- `handleRemove(key, value)` - use key and value to remove a value to the param -- `handleRemoveAll()` - remove all non-default params - -All of these functions act on the react-router history using the `pushHistoryState` function. This causes the query params in the url to update, which in turn triggers change handlers that will re-fetch data for the lists. - -**a note on sort_columns and search_columns** - -We have split out column configuration into separate search and sort column array props--these are passed to the search and sort columns. Both accept an isDefault prop for one of the items in the array to be the default option selected when going to the page. Sort column items can pass an isNumeric boolean in order to chnage the iconography of the sort UI element. Search column items can pass type and if applicable choices, in order to configure the right-hand side of the search bar. - -### FilterTags component - -Similar to the way the list grabs data based on changes to the react-router params, the `FilterTags` component updates when new params are added. This component is a fairly straight-forward map (only slightly complex, because it needed to do a nested map over any values with duplicate keys that were represented by an inner-array). Both key and value are displayed for the tag. - -### qs utility - -The qs (queryset) utility is used to make the search speak the language of the REST API. The main functions of the utilities are to: - -- add, replace and remove filters -- translate filters as url params (for linking and maintaining state), in-memory representation (as JS objects), and params that Django REST Framework understands. - -More info in the below sections: - -#### Encoding for UI vs. API - -For the UI url params, we want to only encode those params that aren't defaults, as the default behavior was defined through configuration and we don't need these in the url as a source of truth. For the API, we need to pass these params so that they are taken into account when the response is built. - -#### mergeParams vs. replaceParams - -**mergeParams** is used to suppport putting values with the same key - -From a UX perspective, we wanted to be able to support searching on the same key multiple times (i.e. searching for things like `?foo=bar&foo=baz`). We do this by creating an array of all values. i.e.: - -``` -{ - foo: ['bar', 'baz'] -} -``` - -Concatenating terms in this way gives you the intersection of both terms (i.e. foo must be "bar" and "baz"). This is helpful for the most-common type of searching, substring (`__icontains`) searches. This will increase filtering, allowing the user to drill-down into the list as terms are added. - -**replaceParams** is used to support sorting, setting page_size, etc. These params only allow one choice, and we need to replace a particular key's value if one is passed. - -#### Working with REST API - -The REST API is coupled with the qs util through the `paramsSerializer`, due to the fact we need axios to support the array for duplicate key values in the object representation of the params to pass to the get request. This is done where axios is configured in the Base.js file, so all requests and request types should support our array syntax for duplicate keys automatically. - -# Advanced Search - this section is a mess, update eventually - -**a note on typing in a smart search query** - -In order to not support a special "language" or "syntax" for crafting the query like we have now (and is the cause of a large amount of bugs), we will not support the old way of typing in a filter like in the current implementation of search. - -Since all search bars are represented in the url, for users who want to input a string to filter results in a single step, typing directly in the url to achieve the filter is acceptable. - -# Advanced search notes - -Current thinking is Advanced Search will be post-3.6, or at least late 3.6 after awx features and "simple search" with the left dropdown and right input for the above phase 1 lists. - -That being said, we want to plan it out so we make sure the infrastructure of how we set up adding/removing tags, what shows up in the url bar, etc. all doesn't have to be redone. - -Users will get to advanced search with a button to the right of search bar. When selected type-ahead key thing opens, left dropdown of search bar goes away, and x is given to get back to regular search (this is in the mockups) - -It is okay to only make this typing representation available initially (i.e. they start doing stuff with the type-ahead and the phases, no more typing in to make a query that way). - -when you click through or type in the search bar for the various phases of crafting the query ("not", "related resource project", "related resource key name", "value foo") which might be represented in the top bar as a series of tags that can be added and removed before submitting the tag. - -We will try to form options data from a static file. Because options data is static, we may be able to generate and store as a static file of some sort (that we can use for managing smart search). Alan had ideas around this. If we do this it will mean we don't have to make a ton of requests as we craft smart search filters. It sounds like the cli may start using something similar. - -## Smart search flow - -Smart search will be able to craft the tag through various states. Note that the phases don't necessarily need to be completed in sequential order. - -PHASE 1: prefix operators - -**TODO: Double check there's no reason we need to include or** and chain** and can just do not\_\_** - -- not\_\_ -- or\_\_ -- chain\_\_ - -how these work: - -To exclude results matching certain criteria, prefix the field parameter with not\_\_: - -?not**field=value -By default, all query string filters are AND'ed together, so only the results matching all filters will be returned. To combine results matching any one of multiple criteria, prefix each query string parameter with or**: - -?or**field=value&or**field=othervalue -?or**not**field=value&or**field=othervalue -(Added in Ansible Controller 1.4.5) The default AND filtering applies all filters simultaneously to each related object being filtered across database relationships. The chain filter instead applies filters separately for each related object. To use, prefix the query string parameter with chain**: - -?chain**related**field=value&chain**related**field2=othervalue -?chain**not**related**field=value&chain**related**field2=othervalue -If the first query above were written as ?related**field=value&related\_\_field2=othervalue, it would return only the primary objects where the same related object satisfied both conditions. As written using the chain filter, it would return the intersection of primary objects matching each condition. - -PHASE 2: related fields, given by array, where \_\_search is appended to them, i.e. - -``` -"related_search_fields": [ - "credentials__search", - "labels__search", - "created_by__search", - "modified_by__search", - "notification_templates__search", - "custom_inventory_scripts__search", - "notification_templates_error__search", - "notification_templates_success__search", - "notification_templates_any__search", - "teams__search", - "projects__search", - "inventories__search", - "applications__search", - "workflows__search", - "instance_groups__search" - ], -``` - -PHASE 3: keys, give by object key names for data.actions.GET - type is given for each key which we could use to help craft the value - -PHASE 4: after key postfix operators can be - -**TODO: will need to figure out which ones we support** - -- exact: Exact match (default lookup if not specified). -- iexact: Case-insensitive version of exact. -- contains: Field contains value. -- icontains: Case-insensitive version of contains. -- startswith: Field starts with value. -- istartswith: Case-insensitive version of startswith. -- endswith: Field ends with value. -- iendswith: Case-insensitive version of endswith. -- regex: Field matches the given regular expression. -- iregex: Case-insensitive version of regex. -- gt: Greater than comparison. -- gte: Greater than or equal to comparison. -- lt: Less than comparison. -- lte: Less than or equal to comparison. -- isnull: Check whether the given field or related object is null; expects a boolean value. -- in: Check whether the given field's value is present in the list provided; expects a list of items. - -PHASE 5: The value. Based on options, we can give hints or validation based on type of value (like number fields don't accept "foo" or whatever) diff --git a/awx/ui/conf.py b/awx/ui/conf.py index 9f1cef04fc09..a666f07dbdfd 100644 --- a/awx/ui/conf.py +++ b/awx/ui/conf.py @@ -8,7 +8,6 @@ from awx.conf import register, fields from awx.ui.fields import PendoTrackingStateField, CustomLogoField # noqa - register( 'PENDO_TRACKING_STATE', field_class=PendoTrackingStateField, @@ -56,16 +55,18 @@ field_class=fields.IntegerField, min_value=100, label=_('Max Job Events Retrieved by UI'), - help_text=_('Maximum number of job events for the UI to retrieve within a ' 'single request.'), + help_text=_('Maximum number of job events for the UI to retrieve within a single request.'), category=_('UI'), category_slug='ui', + hidden=True, ) register( 'UI_LIVE_UPDATES_ENABLED', field_class=fields.BooleanField, label=_('Enable Live Updates in the UI'), - help_text=_('If disabled, the page will not refresh when events are received. ' 'Reloading the page will be required to get the latest details.'), + help_text=_('If disabled, the page will not refresh when events are received. Reloading the page will be required to get the latest details.'), category=_('UI'), category_slug='ui', + hidden=True, ) diff --git a/awx/ui/docs/APP_ARCHITECTURE.md b/awx/ui/docs/APP_ARCHITECTURE.md deleted file mode 100644 index 4be18f17d23e..000000000000 --- a/awx/ui/docs/APP_ARCHITECTURE.md +++ /dev/null @@ -1,27 +0,0 @@ -# Application Architecture - -## Local Storage Integration -The `useStorage` hook integrates with the browser's localStorage api. -It accepts a localStorage key as its only argument and returns a state -variable and setter function for that state variable. The hook enables -bidirectional data transfer between tabs via an event listener that -is registered with the Web Storage api. - - -![Sequence Diagram for useStorage](images/useStorage.png) - -The `useStorage` hook currently lives in the `AppContainer` component. It -can be relocated to a more general location should and if the need -ever arise - -## Session Expiration -Session timeout state is communicated to the client in the HTTP(S) -response headers. Every HTTP(S) response is intercepted to read the -session expiration time before being passed into the rest of the -application. A timeout date is computed from the intercepted HTTP(S) -headers and is pushed into local storage, where it can be read using -standard Web Storage apis or other utilities, such as `useStorage`. - - -![Sequence Diagram for session expiration](images/sessionExpiration.png) - diff --git a/awx/ui/docs/JobOutput.md b/awx/ui/docs/JobOutput.md deleted file mode 100644 index 8cdba2c2b641..000000000000 --- a/awx/ui/docs/JobOutput.md +++ /dev/null @@ -1,64 +0,0 @@ -This document is meant to provide some guidance into the functionality of Job Output and its features. - -## Overview of the feature/screen. Summary of what it does/is - -Joboutput is a feature that allows users to see how their job is doing as it is being run. -This feature displays data sent to the UI via websockets that are connected to several -different endpoints in the API. - -The job output has 2 different states that result in different functionality. One state -is when, the job is actively running. There is limited functionality because of how the -job events are processed when they reach the UI. While the job is running, and -output is coming into the UI, the following features turn off: - -1. [Search](#Search)- The ability to search the output of a job. -2. [Expand/Collapse](#Expand/Collapse)- The ability to expand and collapse job events, tasks, plays, or even the - job itself. The only part of the job ouput that is not collapsable is the playbook summary (only jobs that - are executed from a Job Template have Expand/Collapse functionality). - -The following features are enabled: - -1. Follow/unfollow - `Follow` indicates you are streaming the output on the screen - as it comes into the UI. If you see some output that you want to examine closer while the job is running - scroll to it, and click `Unfollow`, and the output will stop streaming onto the screen. This feature is only - enabled when the job is running and is not complete. If the user scrolls up in the output the UI will unfollow. -2. Page up and page down buttons- Use these buttons to navigate quickly up and down the output. - -![Running job](images/JobOutput-running.png) - -After the job is complete, the Follow/Unfollow button disabled, and Expand/Collapse and Search become enabled. -![Finished job](images/JobOutput-complete.png) - -Not all job types are created equal. Some jobs have a concept of parent-child events. Job events can be inside a Task, -a Task can be inside a Play, and a Play inside a Playbook. Leveraging this concept to enable Expand/Collapse for these -job types, allows you to collapse and hide the children of a particular line of output. This parent-child event -relationship only exists on jobs executed from a job template. All other types of jobs do not -have this event concept, and therefore, do not have Expand/Collapse functionality. By default all job -events are expanded. - -## How output works generally. - -1. Explain the different state components -2. Page up and page down and what’s happening in the background. - -## Different type of job events, and how they relate to the state object - -1. Tasks -2. plays -3. events - -## Non-standard cases - -1. When an event comes into the output that has a parent, but the parent hasn’t arrived yet. -2. When an event with children arrives in output, but the children are not yet present. - -## Expand/Collapse - -### Expand collapse a single event - how it works and how it changes the state object - -### Expand collapse all - how it works and how it changes the state object - -## Search - -1. During job run -2. After job run diff --git a/awx/ui/docs/images/JobOutput-complete.png b/awx/ui/docs/images/JobOutput-complete.png deleted file mode 100644 index 3c076fe305d8..000000000000 Binary files a/awx/ui/docs/images/JobOutput-complete.png and /dev/null differ diff --git a/awx/ui/docs/images/JobOutput-running.png b/awx/ui/docs/images/JobOutput-running.png deleted file mode 100644 index 4e54ce26b2a3..000000000000 Binary files a/awx/ui/docs/images/JobOutput-running.png and /dev/null differ diff --git a/awx/ui/docs/images/sessionExpiration.png b/awx/ui/docs/images/sessionExpiration.png deleted file mode 100644 index fa740c44a5a6..000000000000 Binary files a/awx/ui/docs/images/sessionExpiration.png and /dev/null differ diff --git a/awx/ui/docs/images/useStorage.png b/awx/ui/docs/images/useStorage.png deleted file mode 100644 index 712b47712162..000000000000 Binary files a/awx/ui/docs/images/useStorage.png and /dev/null differ diff --git a/awx/ui/jsconfig.json b/awx/ui/jsconfig.json deleted file mode 100644 index ec2332eb49cc..000000000000 --- a/awx/ui/jsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "src" - } -} diff --git a/awx/ui/package-lock.json b/awx/ui/package-lock.json deleted file mode 100644 index db3110e7af7c..000000000000 --- a/awx/ui/package-lock.json +++ /dev/null @@ -1,39727 +0,0 @@ -{ - "name": "ui", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "ui", - "dependencies": { - "@lingui/react": "3.14.0", - "@patternfly/patternfly": "4.217.1", - "@patternfly/react-core": "^4.264.0", - "@patternfly/react-icons": "4.92.10", - "@patternfly/react-table": "4.108.0", - "ace-builds": "^1.10.1", - "ansi-to-html": "0.7.2", - "axios": "0.27.2", - "codemirror": "^6.0.1", - "d3": "7.6.1", - "dagre": "^0.8.4", - "dompurify": "2.4.0", - "formik": "2.2.9", - "has-ansi": "5.0.1", - "html-entities": "2.3.2", - "js-yaml": "4.1.0", - "luxon": "^3.1.1", - "prop-types": "^15.8.1", - "react": "17.0.2", - "react-ace": "^10.1.0", - "react-dom": "17.0.2", - "react-error-boundary": "^3.1.4", - "react-router-dom": "^5.3.3", - "react-virtualized": "^9.21.1", - "rrule": "2.7.1", - "styled-components": "5.3.6" - }, - "devDependencies": { - "@babel/core": "^7.16.10", - "@babel/eslint-parser": "^7.16.5", - "@babel/eslint-plugin": "^7.16.5", - "@babel/plugin-syntax-jsx": "7.16.7", - "@babel/polyfill": "^7.8.7", - "@babel/preset-react": "7.16.7", - "@cypress/instrument-cra": "^1.4.0", - "@lingui/cli": "^3.7.1", - "@lingui/loader": "3.15.0", - "@lingui/macro": "^3.7.1", - "@nteract/mockument": "^1.0.4", - "@testing-library/jest-dom": "^5.16.2", - "@testing-library/react": "^12.1.5", - "@testing-library/user-event": "14.4.3", - "@wojtekmaj/enzyme-adapter-react-17": "0.6.5", - "babel-plugin-macros": "3.1.0", - "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.14.0", - "enzyme-to-json": "^3.3.5", - "eslint": "^8.7.0", - "eslint-config-airbnb": "19.0.4", - "eslint-config-prettier": "8.3.0", - "eslint-import-resolver-webpack": "0.13.2", - "eslint-plugin-i18next": "5.2.1", - "eslint-plugin-import": "2.25.4", - "eslint-plugin-jsx-a11y": "6.5.1", - "eslint-plugin-react": "7.28.0", - "eslint-plugin-react-hooks": "4.3.0", - "http-proxy-middleware": "^1.0.3", - "jest-websocket-mock": "^2.0.2", - "mock-socket": "^9.1.3", - "prettier": "2.3.2", - "react-scripts": "5.0.1" - }, - "engines": { - "node": ">=16.13.1" - } - }, - "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", - "dev": true - }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.10.tgz", - "integrity": "sha512-pbiIdZbCiMx/MM6toR+OfXarYix3uz0oVsnNtfdAGTcCTu3w/JGF8JhirevXLBJUu0WguSZI12qpKnx7EeMyLA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.10", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/eslint-parser": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.5.tgz", - "integrity": "sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==", - "dev": true, - "dependencies": { - "eslint-scope": "^5.1.1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.11.0", - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/@babel/eslint-plugin": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-plugin/-/eslint-plugin-7.16.5.tgz", - "integrity": "sha512-R1p6RMyU1Xl1U/NNr+D4+HjkQzN5dQOX0MpjW9WLWhHDjhzN9gso96MxxOFvPh0fKF/mMH8TGW2kuqQ2eK2s9A==", - "dev": true, - "dependencies": { - "eslint-rule-composer": "^0.3.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/eslint-parser": ">=7.11.0", - "eslint": ">=7.5.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", - "dependencies": { - "@babel/types": "^7.20.5", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", - "dev": true, - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", - "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", - "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.19.1", - "@babel/helper-split-export-declaration": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz", - "integrity": "sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^4.7.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", - "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", - "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.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.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", - "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz", - "integrity": "sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.19.1", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", - "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.7.tgz", - "integrity": "sha512-7twV3pzhrRxSwHeIvFE6coPgvo+exNDOiGUMg39o2LiLo1Y+4aKpfkcLGcg1UHonzorCt7SNXnoMyCnnIOA8Sw==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", - "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@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==", - "dev": true, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", - "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", - "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", - "dev": true, - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", - "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-flow": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", - "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.13.13.tgz", - "integrity": "sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", - "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz", - "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", - "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", - "dev": true, - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz", - "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", - "dev": true, - "dependencies": { - "regenerator-transform": "^0.14.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", - "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz", - "integrity": "sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.20.2", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/polyfill": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", - "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", - "dev": true, - "dependencies": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.10.tgz", - "integrity": "sha512-iCac3fZn9oOcLqc1N2/copPiX7aoxzsvjeDdXoZobrlbQ6YGgS3bL9HyldOJ8V8AY5P7pFynCATrn7M4dMw0Yg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.7", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", - "@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-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", - "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-transform-react-display-name": "^7.16.7", - "@babel/plugin-transform-react-jsx": "^7.16.7", - "@babel/plugin-transform-react-jsx-development": "^7.16.7", - "@babel/plugin-transform-react-pure-annotations": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz", - "integrity": "sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@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 - }, - "node_modules/@codemirror/autocomplete": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.0.2.tgz", - "integrity": "sha512-9PDjnllmXan/7Uax87KGORbxerDJ/cu10SB+n4Jz0zXMEvIh3+TGgZxhIvDOtaQ4jDBQEM7kHYW4vLdQB0DGZQ==", - "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.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.0.1.tgz", - "integrity": "sha512-iNHDByicYqQjs0Wo1MKGfqNbMYMyhS9WV6EwMVwsHXImlFemgEUC+c5X22bXKBStN3qnwg4fArNZM+gkv22baQ==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.0.tgz", - "integrity": "sha512-tabB0Ef/BflwoEmTB4a//WZ9P90UQyne9qWB9YFsmeS4bnEqSys7UpGk/da1URMXhyfuzWCwp+AQNMhvu8SfnA==", - "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.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.0.0.tgz", - "integrity": "sha512-nUUXcJW1Xp54kNs+a1ToPLK8MadO0rMTnJB8Zk4Z8gBdrN0kqV7uvUraU/T2yqg+grDNR38Vmy/MrhQN/RgwiA==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/search": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.0.0.tgz", - "integrity": "sha512-rL0rd3AhI0TAsaJPUaEwC63KHLO7KL0Z/dYozXj6E7L3wNHRyx7RfE0/j5HsIf912EE5n2PCb4Vg0rGYmDv4UQ==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/state": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.0.tgz", - "integrity": "sha512-qbUr94DZTe6/V1VS7LDLz11rM/1t/nJxR1El4I6UaxDEdc0aZZvq6JCLJWiRmUf95NRAnDH6fhXn+PWp9wGCIg==" - }, - "node_modules/@codemirror/view": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.0.2.tgz", - "integrity": "sha512-mnVT/q1JvKPjpmjXJNeCi/xHyaJ3abGJsumIVpdQ1nE1MXAyHf7GHWt8QpWMUvDiqF0j+inkhVR2OviTdFFX7Q==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "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==", - "dev": 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==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==", - "dev": true - }, - "node_modules/@cypress/instrument-cra": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@cypress/instrument-cra/-/instrument-cra-1.4.0.tgz", - "integrity": "sha512-gXf540xL0jcUXkWyrA2Ug9rzs+jRkc9EPhnRi8XfbnRjdF4lvnn108N6x0lgTApMTbbpCDbVuskHGXDmIuD3CQ==", - "dev": true, - "dependencies": { - "babel-plugin-istanbul": "6.0.0", - "debug": "4.2.0", - "find-yarn-workspace-root": "^2.0.0" - } - }, - "node_modules/@cypress/instrument-cra/node_modules/debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz", - "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==", - "dependencies": { - "@emotion/memoize": "^0.7.4" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", - "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" - }, - "node_modules/@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" - }, - "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" - }, - "node_modules/@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "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/@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz", - "integrity": "sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/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/@jest/console/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/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz", - "integrity": "sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg==", - "dev": true, - "dependencies": { - "@jest/console": "^27.4.6", - "@jest/reporters": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^27.4.2", - "jest-config": "^27.4.7", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-resolve-dependencies": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "jest-watcher": "^27.4.6", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/core/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/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/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/@jest/core/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/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/@jest/core/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/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/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz", - "integrity": "sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/environment/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/environment/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/environment/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/@jest/environment/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/@jest/environment/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/fake-timers": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz", - "integrity": "sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/fake-timers/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/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/@jest/fake-timers/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/@jest/fake-timers/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/fake-timers/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz", - "integrity": "sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/types": "^27.4.2", - "expect": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/globals/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/globals/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/globals/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/@jest/globals/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/@jest/globals/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz", - "integrity": "sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/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/@jest/reporters/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/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/source-map": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz", - "integrity": "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz", - "integrity": "sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ==", - "dev": true, - "dependencies": { - "@jest/console": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/test-result/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/test-result/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/test-result/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/@jest/test-result/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/@jest/test-result/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-result/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz", - "integrity": "sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-runtime": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz", - "integrity": "sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.4.2", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-util": "^27.4.2", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/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/@jest/transform/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/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/types/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" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/types/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/@jest/types/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/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/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/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "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==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@lezer/common": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.0.tgz", - "integrity": "sha512-ohydQe+Hb+w4oMDvXzs8uuJd2NoA3D8YDcLiuDsLqH+yflDTPEpgCsWI3/6rH5C3BAedtH1/R51dxENldQceEA==" - }, - "node_modules/@lezer/highlight": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.0.0.tgz", - "integrity": "sha512-nsCnNtim90UKsB5YxoX65v3GEIw3iCHw9RM2DtdgkiqAbKh9pCdvi8AWNwkYf10Lu6fxNhXPpkpHbW6mihhvJA==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.1.0.tgz", - "integrity": "sha512-Iad04uVwk1PvSnj25mqj7zEEIRAsasbsTRmVzI0AUTs/+1Dz1//iYAaoLr7A+Xa7bZDfql5MKTxZmSlkYZD3Dg==", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@lingui/babel-plugin-extract-messages": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.15.0.tgz", - "integrity": "sha512-iMQmJIkC18Zwc/IDpm3Oclj3KMDQuvipCS2yVHr0MyaeOCeOZ3ZoLVeaa8pfE5pImzlHJ0ss8RRm/St54JElhw==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.11.6", - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.15.0", - "mkdirp": "^1.0.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@lingui/cli": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.15.0.tgz", - "integrity": "sha512-6arKc0Mc1z3ABHobjPkhViV+7VUjBhwFwoU0VlT7HBmtrOYad9CBwWEiD+oiEhiHYzLTR7lHTVf674IjTuVvJQ==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.11.6", - "@babel/parser": "^7.11.5", - "@babel/plugin-syntax-jsx": "^7.10.4", - "@babel/runtime": "^7.11.2", - "@babel/types": "^7.11.5", - "@lingui/babel-plugin-extract-messages": "^3.15.0", - "@lingui/conf": "^3.15.0", - "babel-plugin-macros": "^3.0.1", - "bcp-47": "^1.0.7", - "chalk": "^4.1.0", - "chokidar": "3.5.1", - "cli-table": "0.3.6", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fs-extra": "^9.0.1", - "fuzzaldrin": "^2.1.0", - "glob": "^7.1.4", - "inquirer": "^7.3.3", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3", - "micromatch": "4.0.2", - "mkdirp": "^1.0.4", - "node-gettext": "^3.0.0", - "normalize-path": "^3.0.0", - "ora": "^5.1.0", - "papaparse": "^5.3.0", - "pkg-up": "^3.1.0", - "plurals-cldr": "^1.0.4", - "pofile": "^1.1.0", - "pseudolocale": "^1.1.0", - "ramda": "^0.27.1" - }, - "bin": { - "lingui": "lingui.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "babel-plugin-macros": "2 || 3", - "typescript": "2 || 3 || 4" - } - }, - "node_modules/@lingui/cli/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" - } - }, - "node_modules/@lingui/cli/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@lingui/cli/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/@lingui/cli/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/@lingui/cli/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/@lingui/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@lingui/cli/node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@lingui/cli/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/@lingui/conf": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.15.0.tgz", - "integrity": "sha512-gDGBbqWo6+B3PNjxTGl2asVdd8hC6w+iGsEPonvMw7GFmXb99qybBGdV2ofDlwlT9vChcPwMVtrYE6H0fTZuzA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.11.2", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "cosmiconfig-typescript-loader": "^2.0.1", - "jest-validate": "^26.5.2", - "lodash.get": "^4.4.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@lingui/conf/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" - } - }, - "node_modules/@lingui/conf/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@lingui/conf/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/@lingui/conf/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/@lingui/conf/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@lingui/conf/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/@lingui/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.14.0.tgz", - "integrity": "sha512-ertREq9oi9B/umxpd/pInm9uFO8FLK2/0FXfDmMqvH5ydswWn/c9nY5YO4W1h4/8LWO45mewypOIyjoue4De1w==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@lingui/loader": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-3.15.0.tgz", - "integrity": "sha512-Yg7KhinDQmRfqr51bofvD50CuzC1rF8nlFoPsZgLSjpIn4xKdzoCpRaRftRc6sOS5EnoKaK92QwXNv2Ayalz6A==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/cli": "^3.15.0", - "@lingui/conf": "^3.15.0", - "loader-utils": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/@lingui/macro": { - "version": "3.8.10", - "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.10.tgz", - "integrity": "sha512-oZZ/F7HsNQkDsnHFroxzGFuEIXM624H72RIj8j2ClpR64nt+xYDxXYC6TYFicQLtBGcKKBTBoM+zbDaoIv74qQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.8.10", - "ramda": "^0.27.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@lingui/react": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/react/-/react-3.14.0.tgz", - "integrity": "sha512-ow9Mtru7f0T2S9AwnPWRejppcucCW0LmoDR3P4wqHjL+eH5f8a6nxd2doxGieC91/2i4qqW88y4K/zXJxwRSQw==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@lingui/core": "^3.14.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.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==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nteract/mockument": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@nteract/mockument/-/mockument-1.0.4.tgz", - "integrity": "sha1-9/hf2T5Dgo7HQcX0xXMRgu2w7LI=", - "dev": true - }, - "node_modules/@patternfly/patternfly": { - "version": "4.217.1", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.217.1.tgz", - "integrity": "sha512-uN7JgfQsyR16YHkuGRCTIcBcnyKIqKjGkB2SGk9x1XXH3yYGenL83kpAavX9Xtozqp17KppOlybJuzcKvZMrgw==" - }, - "node_modules/@patternfly/react-core": { - "version": "4.264.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.264.0.tgz", - "integrity": "sha512-tK0BMWxw8nhukev40HZ6q6d02pDnjX7oyA91vHa18aakJUKBWMaerqpG4NZVMoh0tPKX3aLNj+zyCwDALFAZZw==", - "dependencies": { - "@patternfly/react-icons": "^4.93.0", - "@patternfly/react-styles": "^4.92.0", - "@patternfly/react-tokens": "^4.94.0", - "focus-trap": "6.9.2", - "react-dropzone": "9.0.0", - "tippy.js": "5.1.2", - "tslib": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17 || ^18", - "react-dom": "^16.8 || ^17 || ^18" - } - }, - "node_modules/@patternfly/react-core/node_modules/@patternfly/react-icons": { - "version": "4.93.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.93.0.tgz", - "integrity": "sha512-OH0vORVioL+HLWMEog8/3u8jsiMCeJ0pFpvRKRhy5Uk4CdAe40k1SOBvXJP6opr+O8TLbz0q3bm8Jsh/bPaCuQ==", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18", - "react-dom": "^16.8 || ^17 || ^18" - } - }, - "node_modules/@patternfly/react-core/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/@patternfly/react-icons": { - "version": "4.92.10", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.92.10.tgz", - "integrity": "sha512-vwCy7b+OyyuvLDSLqLUG2DkJZgMDogjld8tJTdAaG8HiEhC1sJPZac+5wD7AuS3ym/sQolS4vYtNiVDnMEORxA==", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18", - "react-dom": "^16.8 || ^17 || ^18" - } - }, - "node_modules/@patternfly/react-styles": { - "version": "4.92.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.92.0.tgz", - "integrity": "sha512-B/f6iyu8UEN1+wRxdC4sLIhvJeyL8SqInDXZmwOIqK8uPJ8Lze7qrbVhkkVzbMF37/oDPVa6dZH8qZFq062LEA==" - }, - "node_modules/@patternfly/react-table": { - "version": "4.108.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-4.108.0.tgz", - "integrity": "sha512-EUvd3rlkE1UXobAm7L6JHgNE3TW8IYTaVwwH/px4Mkn5mBayDO6f+w6QM3OeoDQVZcXK6IYFe7QQaYd/vWIJCQ==", - "dependencies": { - "@patternfly/react-core": "^4.239.0", - "@patternfly/react-icons": "^4.90.0", - "@patternfly/react-styles": "^4.89.0", - "@patternfly/react-tokens": "^4.91.0", - "lodash": "^4.17.19", - "tslib": "^2.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" - } - }, - "node_modules/@patternfly/react-table/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "node_modules/@patternfly/react-tokens": { - "version": "4.94.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.94.0.tgz", - "integrity": "sha512-fYXxUJZnzpn89K2zzHF0cSncZZVGKrohdb5f5T1wzxwU2NZPVGpvr88xhm+V2Y/fSrrTPwXcP3IIdtNOOtJdZw==" - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.4.tgz", - "integrity": "sha512-zZbZeHQDnoTlt2AF+diQT0wsSXpvWiaIOZwBRdltNFhG1+I3ozyaw7U/nBiUwyJ0D+zwdXp0E3bWOl38Ag2BMw==", - "dev": true, - "dependencies": { - "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", - "core-js-pure": "^3.8.1", - "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <3.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin/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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin/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/@pmmmwh/react-refresh-webpack-plugin/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/@pmmmwh/react-refresh-webpack-plugin/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/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@rollup/plugin-babel": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", - "integrity": "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/plugin-node-resolve/node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/pluginutils/node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", - "dev": true, - "dependencies": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "dev": true, - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "dev": true, - "dependencies": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/core/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.12.6" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "dev": true, - "dependencies": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@svgr/plugin-svgo/node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@testing-library/dom": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.3.tgz", - "integrity": "sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^4.2.0", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/node_modules/aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/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/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", - "dev": true, - "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/jest-dom/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/@testing-library/jest-dom/node_modules/aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/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/@testing-library/jest-dom/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/@testing-library/jest-dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/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/@testing-library/react": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", - "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.0.0", - "@types/react-dom": "<18.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": "<18.0.0", - "react-dom": "<18.0.0" - } - }, - "node_modules/@testing-library/user-event": { - "version": "14.4.3", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", - "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", - "dev": true, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "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==", - "dev": 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==", - "dev": 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==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cheerio": { - "version": "0.22.28", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.28.tgz", - "integrity": "sha512-ehUMGSW5IeDxJjbru4awKYMlKGmo1wSSGUVqXtYwlgmUM8X1a0PZttEIm6yEY7vHsY/hh6iPnklF213G0UColw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.2.tgz", - "integrity": "sha512-nQxgB8/Sg+QKhnV8e0WzPpxjIGT3tuJDDzybkDi8ItE/IgTlHo07U0shaIjzhcvQxlq9SDRE42lsJ23uvEgJ2A==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true - }, - "node_modules/@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true - }, - "node_modules/@types/http-proxy": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz", - "integrity": "sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "27.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", - "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", - "dev": true, - "dependencies": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "node_modules/@types/jest/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/@types/jest/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/@types/jest/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/@types/jest/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/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "node_modules/@types/node": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", - "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz", - "integrity": "sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w==", - "dev": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", - "dev": true - }, - "node_modules/@types/q": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", - "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "node_modules/@types/react": { - "version": "17.0.41", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.41.tgz", - "integrity": "sha512-chYZ9ogWUodyC7VUTRBfblysKLjnohhFY9bGLwvnUFFy48+vB9DikmB3lW0qTFmBcKSzmdglcvkHK71IioOlDA==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "17.0.14", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.14.tgz", - "integrity": "sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", - "dev": true - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.3", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz", - "integrity": "sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw==", - "dev": true, - "dependencies": { - "@types/jest": "*" - } - }, - "node_modules/@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", - "dev": true - }, - "node_modules/@types/ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "15.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", - "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", - "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz", - "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.45.0", - "@typescript-eslint/type-utils": "5.45.0", - "@typescript-eslint/utils": "5.45.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.0.tgz", - "integrity": "sha512-DnRQg5+3uHHt/gaifTjwg9OKbg9/TWehfJzYHQIDJboPEbF897BKDE/qoqMhW7nf0jWRV1mwVXTaUvtB1/9Gwg==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.45.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz", - "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.45.0", - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/typescript-estree": "5.45.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz", - "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/visitor-keys": "5.45.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz", - "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.45.0", - "@typescript-eslint/utils": "5.45.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz", - "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz", - "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/visitor-keys": "5.45.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz", - "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.45.0", - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/typescript-estree": "5.45.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz", - "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.45.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@wojtekmaj/enzyme-adapter-react-17": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@wojtekmaj/enzyme-adapter-react-17/-/enzyme-adapter-react-17-0.6.5.tgz", - "integrity": "sha512-ChIObUiXXYUiqzXPqOai+p6KF5dlbItpDDYsftUOQiAiygbMDlLeJIjynC6ZrJIa2U2MpRp4YJmtR2GQyIHjgA==", - "dev": true, - "dependencies": { - "@wojtekmaj/enzyme-adapter-utils": "^0.1.1", - "enzyme-shallow-equal": "^1.0.0", - "has": "^1.0.0", - "object.assign": "^4.1.0", - "object.values": "^1.1.0", - "prop-types": "^15.7.0", - "react-is": "^17.0.2", - "react-test-renderer": "^17.0.0" - }, - "peerDependencies": { - "enzyme": "^3.0.0", - "react": "^17.0.0-0", - "react-dom": "^17.0.0-0" - } - }, - "node_modules/@wojtekmaj/enzyme-adapter-react-17/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/@wojtekmaj/enzyme-adapter-utils": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@wojtekmaj/enzyme-adapter-utils/-/enzyme-adapter-utils-0.1.1.tgz", - "integrity": "sha512-bNPWtN/d8huKOkC6j1E3EkSamnRrHHT7YuR6f9JppAQqtoAm3v4/vERe4J14jQKmHLCyEBHXrlgb7H6l817hVg==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.0", - "has": "^1.0.0", - "object.assign": "^4.1.0", - "object.fromentries": "^2.0.0", - "prop-types": "^15.7.0" - }, - "peerDependencies": { - "react": "^17.0.0-0" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ace-builds": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.10.1.tgz", - "integrity": "sha512-w8Xj6lZUtOYAquVYvdpZhb0GxXrZ+qpVfgj5LP2FwUbXE8fPrCmfu86FjwOiSphx/8PMbXXVldFLD2+RIXayyA==" - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "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-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", - "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "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" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/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/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "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" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "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/ansi-to-html": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", - "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", - "dependencies": { - "entities": "^2.2.0" - }, - "bin": { - "ansi-to-html": "bin/ansi-to-html" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "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/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/array-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", - "dev": true - }, - "node_modules/array-find": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", - "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", - "dev": true - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.find": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.2.tgz", - "integrity": "sha512-00S1O4ewO95OmmJW7EesWfQlrCrLEL8kZ40w3+GkLX2yTt0m2ggcePPa2uHPJ9KUmJvwRq+lCV9bD8Yim23x/Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, - "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": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "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, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/attr-accept": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", - "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", - "dependencies": { - "core-js": "^2.5.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", - "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", - "dev": true, - "dependencies": { - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001297", - "fraction.js": "^4.1.2", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axe-core": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", - "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "node_modules/babel-jest": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz", - "integrity": "sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg==", - "dev": true, - "dependencies": { - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.4.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-jest/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/babel-jest/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/babel-jest/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/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/babel-jest/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/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/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/babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", - "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/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/babel-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz", - "integrity": "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "dev": true, - "peerDependencies": { - "@babel/core": "^7.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz", - "integrity": "sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.20.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-styled-components": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz", - "integrity": "sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-module-imports": "^7.0.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "lodash": "^4.17.11" - } - }, - "node_modules/babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" - }, - "node_modules/babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", - "dev": true - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz", - "integrity": "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^27.4.0", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "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==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "node_modules/bcp-47": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", - "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "node_modules/bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", - "dev": true, - "dependencies": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.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==", - "dev": true - }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/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, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "node_modules/builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camel-case/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, - "node_modules/caniuse-api": { - "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==", - "dev": true, - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001434", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", - "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/check-types": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", - "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==", - "dev": true - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz", - "integrity": "sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng==", - "dev": true, - "dependencies": { - "cheerio-select": "^1.4.0", - "dom-serializer": "^1.3.1", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cheerio-select": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.4.0.tgz", - "integrity": "sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==", - "dev": true, - "dependencies": { - "css-select": "^4.1.2", - "css-what": "^5.0.0", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0" - } - }, - "node_modules/cheerio/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "node_modules/clean-css": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", - "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", - "dev": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", - "dev": true, - "dependencies": { - "colors": "1.0.3" - }, - "engines": { - "node": ">= 0.2.0" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "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/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, - "node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, - "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" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/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/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "hasInstallScript": true - }, - "node_modules/core-js-compat": { - "version": "3.26.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", - "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", - "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==", - "dev": true, - "hasInstallScript": 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": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", - "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", - "dev": true, - "dependencies": { - "cosmiconfig": "^7", - "ts-node": "^10.8.1" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=7", - "typescript": ">=3" - } - }, - "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==", - "dev": true - }, - "node_modules/crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" - }, - "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/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/css-blank-pseudo": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.2.tgz", - "integrity": "sha512-hOb1LFjRR+8ocA071xUSmg5VslJ8NGo/I2qpUpdeAYyBVCgupS5O8SEVo4SxEMYyFBNodBkzG3T1iqW9HCXxew==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=", - "engines": { - "node": ">=4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz", - "integrity": "sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw==", - "dev": true, - "dependencies": { - "timsort": "^0.3.0" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.3.tgz", - "integrity": "sha512-0gDYWEKaGacwxCqvQ3Ypg6wGdD1AztbMm5h1JsactG2hP2eiflj808QITmuWBpE7sjSEVrAlZhPTVd/nNMj/hQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "dev": true, - "dependencies": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/css-minimizer-webpack-plugin/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/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.2.tgz", - "integrity": "sha512-gv0KQBEM+q/XdoKyznovq3KW7ocO7k+FhPP+hQR1MenJdu0uPGS6IZa9PzlbqBeS6XcZJNAoqoFxlAUW461CrA==", - "dev": true, - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "node_modules/css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", - "dev": true - }, - "node_modules/cssdb": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", - "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", - "dev": 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==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.15.tgz", - "integrity": "sha512-ppZsS7oPpi2sfiyV5+i+NbB/3GtQ+ab2Vs1azrZaXWujUSN4o+WdTxlCZIMcT9yLW3VO/5yX3vpyDaQ1nIn8CQ==", - "dev": true, - "dependencies": { - "cssnano-preset-default": "^5.1.10", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-preset-default": { - "version": "5.1.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.10.tgz", - "integrity": "sha512-BcpSzUVygHMOnp9uG5rfPzTOCb0GAHQkqtUQx8j1oMNF9A1Q8hziOOhiM4bdICpmrBIU85BE64RD5XGYsVQZNA==", - "dev": true, - "dependencies": { - "css-declaration-sorter": "^6.0.3", - "cssnano-utils": "^3.0.0", - "postcss-calc": "^8.2.0", - "postcss-colormin": "^5.2.3", - "postcss-convert-values": "^5.0.2", - "postcss-discard-comments": "^5.0.1", - "postcss-discard-duplicates": "^5.0.1", - "postcss-discard-empty": "^5.0.1", - "postcss-discard-overridden": "^5.0.2", - "postcss-merge-longhand": "^5.0.4", - "postcss-merge-rules": "^5.0.4", - "postcss-minify-font-values": "^5.0.2", - "postcss-minify-gradients": "^5.0.4", - "postcss-minify-params": "^5.0.3", - "postcss-minify-selectors": "^5.1.1", - "postcss-normalize-charset": "^5.0.1", - "postcss-normalize-display-values": "^5.0.2", - "postcss-normalize-positions": "^5.0.2", - "postcss-normalize-repeat-style": "^5.0.2", - "postcss-normalize-string": "^5.0.2", - "postcss-normalize-timing-functions": "^5.0.2", - "postcss-normalize-unicode": "^5.0.2", - "postcss-normalize-url": "^5.0.4", - "postcss-normalize-whitespace": "^5.0.2", - "postcss-ordered-values": "^5.0.3", - "postcss-reduce-initial": "^5.0.2", - "postcss-reduce-transforms": "^5.0.2", - "postcss-svgo": "^5.0.3", - "postcss-unique-selectors": "^5.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.0.tgz", - "integrity": "sha512-Pzs7/BZ6OgT+tXXuF12DKR8SmSbzUeVYCtMBbS8lI0uAm3mrYmkyqCXXPsQESI6kmLfEVBppbdVY/el3hg3nAA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dev": true, - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/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==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/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==", - "dev": true - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" - }, - "node_modules/d3": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", - "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", - "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dependencies": { - "d3-path": "1 - 3" - }, - "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==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", - "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", - "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv/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/d3-dsv/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.0.1.tgz", - "integrity": "sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz", - "integrity": "sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw==", - "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==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "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==", - "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-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.0.1.tgz", - "integrity": "sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.0.0.tgz", - "integrity": "sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==", - "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==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dagre": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", - "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", - "dependencies": { - "graphlib": "^2.1.8", - "lodash": "^4.17.15" - } - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", - "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==", - "dev": true - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/date-fns": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.21.3.tgz", - "integrity": "sha512-HeYdzCaFflc1i4tGbj7JKMjM4cKGYoyxwcIIkHzNgCkX8xXDNJDZXgDDVchIWpN4eQc3lH37WarduXFZJOtxfw==", - "dev": true, - "engines": { - "node": ">=0.11" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "node_modules/deepmerge": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", - "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "dependencies": { - "robust-predicates": "^3.0.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "dev": true, - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/detective": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", - "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", - "dev": true, - "dependencies": { - "acorn-node": "^1.6.1", - "defined": "^1.0.0", - "minimist": "^1.1.1" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "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==" - }, - "node_modules/diff-sequences": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", - "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", - "dev": true - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "node_modules/dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "dependencies": { - "buffer-indexof": "^1.0.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz", - "integrity": "sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==", - "dev": true - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "dependencies": { - "utila": "~0.4" - } - }, - "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==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", - "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "entities": "^2.0.0" - } - }, - "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==" - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dot-case/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "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==", - "dev": true - }, - "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - }, - "engines": { - "node": ">=0.6" - } - }, - "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/enzyme": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz", - "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==", - "dev": true, - "dependencies": { - "array.prototype.flat": "^1.2.3", - "cheerio": "^1.0.0-rc.3", - "enzyme-shallow-equal": "^1.0.1", - "function.prototype.name": "^1.1.2", - "has": "^1.0.3", - "html-element-map": "^1.2.0", - "is-boolean-object": "^1.0.1", - "is-callable": "^1.1.5", - "is-number-object": "^1.0.4", - "is-regex": "^1.0.5", - "is-string": "^1.0.5", - "is-subset": "^0.1.1", - "lodash.escape": "^4.0.1", - "lodash.isequal": "^4.5.0", - "object-inspect": "^1.7.0", - "object-is": "^1.0.2", - "object.assign": "^4.1.0", - "object.entries": "^1.1.1", - "object.values": "^1.1.1", - "raf": "^3.4.1", - "rst-selector-parser": "^2.2.3", - "string.prototype.trim": "^1.2.1" - } - }, - "node_modules/enzyme-adapter-react-16": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz", - "integrity": "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==", - "dev": true, - "dependencies": { - "enzyme-adapter-utils": "^1.14.0", - "enzyme-shallow-equal": "^1.0.4", - "has": "^1.0.3", - "object.assign": "^4.1.2", - "object.values": "^1.1.2", - "prop-types": "^15.7.2", - "react-is": "^16.13.1", - "react-test-renderer": "^16.0.0-0", - "semver": "^5.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - }, - "peerDependencies": { - "enzyme": "^3.0.0", - "react": "^16.0.0-0", - "react-dom": "^16.0.0-0" - } - }, - "node_modules/enzyme-adapter-react-16/node_modules/airbnb-prop-types": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz", - "integrity": "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==", - "dev": true, - "dependencies": { - "array.prototype.find": "^2.1.1", - "function.prototype.name": "^1.1.2", - "is-regex": "^1.1.0", - "object-is": "^1.1.2", - "object.assign": "^4.1.0", - "object.entries": "^1.1.2", - "prop-types": "^15.7.2", - "prop-types-exact": "^1.2.0", - "react-is": "^16.13.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - }, - "peerDependencies": { - "react": "^0.14 || ^15.0.0 || ^16.0.0-alpha" - } - }, - "node_modules/enzyme-adapter-react-16/node_modules/enzyme-adapter-utils": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz", - "integrity": "sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg==", - "dev": true, - "dependencies": { - "airbnb-prop-types": "^2.16.0", - "function.prototype.name": "^1.1.3", - "has": "^1.0.3", - "object.assign": "^4.1.2", - "object.fromentries": "^2.0.3", - "prop-types": "^15.7.2", - "semver": "^5.7.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - }, - "peerDependencies": { - "react": "0.13.x || 0.14.x || ^15.0.0-0 || ^16.0.0-0" - } - }, - "node_modules/enzyme-adapter-react-16/node_modules/react-test-renderer": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz", - "integrity": "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "react-is": "^16.8.6", - "scheduler": "^0.19.1" - }, - "peerDependencies": { - "react": "^16.14.0" - } - }, - "node_modules/enzyme-adapter-react-16/node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/enzyme-adapter-react-16/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/enzyme-shallow-equal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz", - "integrity": "sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==", - "dev": true, - "dependencies": { - "has": "^1.0.3", - "object-is": "^1.1.2" - } - }, - "node_modules/enzyme-to-json": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.6.2.tgz", - "integrity": "sha512-Ynm6Z6R6iwQ0g2g1YToz6DWhxVnt8Dy1ijR2zynRKxTyBGA8rCDXU3rs2Qc4OKvUvc2Qoe1bcFK6bnPs20TrTg==", - "dev": true, - "dependencies": { - "@types/cheerio": "^0.22.22", - "lodash": "^4.17.21", - "react-is": "^16.12.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", - "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", - "dev": true, - "dependencies": { - "stackframe": "^1.1.1" - } - }, - "node_modules/es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "node_modules/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" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.0.5", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "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.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-airbnb": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", - "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", - "dev": true, - "dependencies": { - "eslint-config-airbnb-base": "^15.0.0", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5" - }, - "engines": { - "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.28.0", - "eslint-plugin-react-hooks": "^4.3.0" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-webpack": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.2.tgz", - "integrity": "sha512-XodIPyg1OgE2h5BDErz3WJoK7lawxKTJNhgPNafRST6csC/MZC+L5P6kKqsZGRInpbgc02s/WZMrb4uGJzcuRg==", - "dev": true, - "dependencies": { - "array-find": "^1.0.0", - "debug": "^3.2.7", - "enhanced-resolve": "^0.9.1", - "find-root": "^1.1.0", - "has": "^1.0.3", - "interpret": "^1.4.0", - "is-core-module": "^2.7.0", - "is-regex": "^1.1.4", - "lodash": "^4.17.21", - "resolve": "^1.20.0", - "semver": "^5.7.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "eslint-plugin-import": ">=1.4.0", - "webpack": ">=1.11.0" - } - }, - "node_modules/eslint-import-resolver-webpack/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/eslint-import-resolver-webpack/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", - "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "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/eslint-module-utils/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@babel/plugin-syntax-flow": "^7.14.5", - "@babel/plugin-transform-react-jsx": "^7.14.9", - "eslint": "^8.1.0" - } - }, - "node_modules/eslint-plugin-i18next": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-5.2.1.tgz", - "integrity": "sha512-yXlWOMiyWz9aCGVrLeFijt+LsCXZj9QoddYXmxUeFZrqst4Z2j6vAMBn2iSE2JTNbPDyrdGl3H03UCo+CbdKbQ==", - "dev": true, - "dependencies": { - "requireindex": "~1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", - "has": "^1.0.3", - "is-core-module": "^2.8.0", - "is-glob": "^4.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/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==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", - "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.16.3", - "aria-query": "^4.2.2", - "array-includes": "^3.1.4", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.3.5", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.7", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.2.1", - "language-tags": "^1.0.5", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/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/eslint-plugin-react": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz", - "integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.0", - "object.values": "^1.1.5", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", - "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-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==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "node_modules/eslint-plugin-testing-library": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", - "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^5.13.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0", - "npm": ">=6" - }, - "peerDependencies": { - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/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" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/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/eslint/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/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/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==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/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/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, - "engines": { - "node": ">=10" - } - }, - "node_modules/espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", - "dev": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/espree/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.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==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz", - "integrity": "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "jest-get-type": "^27.4.0", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/expect/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/expect/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/expect/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/expect/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/expect/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/expect/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/expect/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/expect/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/expect/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/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "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": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "node_modules/express/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/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/file-selector": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", - "integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/file-selector/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/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/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/finalhandler/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "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/find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dev": true, - "dependencies": { - "micromatch": "^4.0.2" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/focus-trap": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz", - "integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==", - "dependencies": { - "tabbable": "^5.3.2" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", - "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formik": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz", - "integrity": "sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==", - "funding": [ - { - "type": "individual", - "url": "https://opencollective.com/formik" - } - ], - "dependencies": { - "deepmerge": "^2.1.1", - "hoist-non-react-statics": "^3.3.0", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "react-fast-compare": "^2.0.1", - "tiny-warning": "^1.0.2", - "tslib": "^1.10.0" - }, - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "dev": true, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "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, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/function.prototype.name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.4.tgz", - "integrity": "sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz", - "integrity": "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==", - "dev": true - }, - "node_modules/fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "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==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "node_modules/graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", - "dependencies": { - "lodash": "^4.17.15" - } - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dev": true, - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-5.0.1.tgz", - "integrity": "sha512-Fp2IsZDnnyoJkKg22ZyQFvD7QRCcMTsLAtloKXyXWJ1joGLtItRU9Bv/k1o0tELL2NF3ZZBcycSKryZUM+Yl3g==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/has-ansi?sponsor=1" - } - }, - "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-element-map": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.0.tgz", - "integrity": "sha512-AqCt/m9YaiMwaaAyOPdq4Ga0cM+jdDWWGueUMkdROZcTeClaGpN0AQeyGchZhTegQoABmc6+IqH7oCR/8vhQYg==", - "dev": true, - "dependencies": { - "array-filter": "^1.0.0", - "call-bind": "^1.0.2" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==" - }, - "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-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "dev": true, - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" - } - }, - "node_modules/html-webpack-plugin/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", - "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", - "dev": true, - "dependencies": { - "@types/http-proxy": "^1.17.5", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/idb": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", - "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==", - "dev": true - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=", - "dev": true, - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, - "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 - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "9.0.16", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", - "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "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, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "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": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, - "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" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/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/jake/node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/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/jake/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/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz", - "integrity": "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==", - "dev": true, - "dependencies": { - "@jest/core": "^27.4.7", - "import-local": "^3.0.2", - "jest-cli": "^27.4.7" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz", - "integrity": "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-changed-files/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-changed-files/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/jest-changed-files/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/jest-changed-files/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz", - "integrity": "sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus/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/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/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/jest-circus/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/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/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/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz", - "integrity": "sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw==", - "dev": true, - "dependencies": { - "@jest/core": "^27.4.7", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "jest-config": "^27.4.7", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-cli/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/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/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/jest-cli/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/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-cli/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/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/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz", - "integrity": "sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.4.6", - "@jest/types": "^27.4.2", - "babel-jest": "^27.4.6", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-circus": "^27.4.6", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-jasmine2": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-config/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/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/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/jest-config/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/jest-config/node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/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/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz", - "integrity": "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.4.0", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-diff/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/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/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/jest-diff/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/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "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/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz", - "integrity": "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz", - "integrity": "sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-each/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/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/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/jest-each/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/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/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/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz", - "integrity": "sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-jsdom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/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/jest-environment-jsdom/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/jest-environment-jsdom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz", - "integrity": "sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-node/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-node/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/jest-environment-node/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/jest-environment-node/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-haste-map": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz", - "integrity": "sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^27.4.0", - "jest-serializer": "^27.4.0", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-haste-map/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-haste-map/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/jest-haste-map/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/jest-haste-map/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz", - "integrity": "sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-jasmine2/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/jest-jasmine2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-jasmine2/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/jest-jasmine2/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/jest-jasmine2/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-jasmine2/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/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/jest-jasmine2/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz", - "integrity": "sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA==", - "dev": true, - "dependencies": { - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector/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/jest-leak-detector/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-leak-detector/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/jest-matcher-utils": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz", - "integrity": "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils/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/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/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/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz", - "integrity": "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.4.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-message-util/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/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/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/jest-message-util/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/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/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/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz", - "integrity": "sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-mock/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-mock/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-mock/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/jest-mock/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/jest-mock/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz", - "integrity": "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz", - "integrity": "sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz", - "integrity": "sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "jest-regex-util": "^27.4.0", - "jest-snapshot": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/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/jest-resolve-dependencies/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/jest-resolve-dependencies/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve/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/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/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/jest-resolve/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/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-resolve/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/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/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz", - "integrity": "sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg==", - "dev": true, - "dependencies": { - "@jest/console": "^27.4.6", - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-docblock": "^27.4.0", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-haste-map": "^27.4.6", - "jest-leak-detector": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/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/jest-runner/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/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz", - "integrity": "sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/globals": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/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/jest-runtime/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/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-serializer": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", - "integrity": "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz", - "integrity": "sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-haste-map": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "natural-compare": "^1.4.0", - "pretty-format": "^27.4.6", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-snapshot/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/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/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/jest-snapshot/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/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "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/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/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/jest-snapshot/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz", - "integrity": "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==", - "dev": true, - "dependencies": { - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.4", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-util/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-util/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/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/jest-util/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/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-validate/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" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-validate/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/jest-validate/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/jest-validate/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.0.0.tgz", - "integrity": "sha512-jxoszalAb394WElmiJTFBMzie/RDCF+W7Q29n5LzOPtcoQoHWfdUtHFkbhgf5NwWe8uMOxvKb/g7ea7CshfkTw==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^27.0.0", - "jest-watcher": "^27.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "jest": "^27.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/char-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.0.tgz", - "integrity": "sha512-oGu2QekBMXgyQNWPDRQ001bjvDnZe4/zBTz37TMbiKz1NbNiyiH5hRkobe7npRN6GfbGbxMYFck/vQ1r9c1VMA==", - "dev": true, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/jest-watch-typeahead/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/jest-watch-typeahead/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/jest-watch-typeahead/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "dev": true, - "dependencies": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/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/jest-watch-typeahead/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz", - "integrity": "sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.4.2", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/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/jest-watcher/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/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-websocket-mock": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jest-websocket-mock/-/jest-websocket-mock-2.2.0.tgz", - "integrity": "sha512-lc3wwXOEyNa4ZpcgJtUG3mmKMAq5FAsKYiZph0p/+PAJrAPuX4JCIfJMdJ/urRsLBG51fwm/wlVPNbR6s2nzNw==", - "dev": true - }, - "node_modules/jest-worker": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", - "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/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" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "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==", - "dev": true - }, - "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-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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.0.tgz", - "integrity": "sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", - "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.3", - "object.assign": "^4.1.2" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", - "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", - "dev": true, - "dependencies": { - "language-subtag-registry": "~0.3.2" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "node_modules/lodash.escape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", - "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=", - "dev": true - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "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.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "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" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lower-case/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "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/luxon": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", - "integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", - "dev": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true - }, - "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==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", - "dev": true, - "dependencies": { - "fs-monkey": "1.0.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", - "dev": true - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dependencies": { - "mime-db": "1.51.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/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - }, - "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.2.tgz", - "integrity": "sha512-Lwgq9qLNyBK6yNLgzssXnq4r2+mB9Mz3cJWlM8kseysHIvTicFhDNimFgY94jjqlwhNzLPsq8wv4X+vOHtMdYA==", - "dev": true, - "dependencies": { - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/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/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "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.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mock-socket": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.1.3.tgz", - "integrity": "sha512-uz8lx8c5wuJYJ21f5UtovqpV0+KJuVwE7cVOLNhrl2QW/CvmstOLRfjXnLSbfFHZtJtiaSGQu0oCJA8SmRcK6A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/moo": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", - "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==", - "dev": true - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "dependencies": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "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/nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", - "dev": true, - "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": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/nearley": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", - "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", - "dev": true, - "dependencies": { - "commander": "^2.19.0", - "moo": "^0.5.0", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6" - }, - "bin": { - "nearley-railroad": "bin/nearley-railroad.js", - "nearley-test": "bin/nearley-test.js", - "nearley-unparse": "bin/nearley-unparse.js", - "nearleyc": "bin/nearleyc.js" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/no-case/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dev": true, - "dependencies": { - "lodash.get": "^4.4.2" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "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/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true, - "engines": { - "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==", - "dev": true, - "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", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", - "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" - } - }, - "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.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", - "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "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" - } - }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", - "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" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", - "dev": true, - "dependencies": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "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/papaparse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.0.tgz", - "integrity": "sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==", - "dev": true - }, - "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/param-case/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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/pascal-case/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, - "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/pirates": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", - "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/plurals-cldr": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/plurals-cldr/-/plurals-cldr-1.0.4.tgz", - "integrity": "sha512-4nLXqtel7fsCgzi8dvRZvUjfL8SXpP982sKg7b2TgpnR8rDnes06iuQ83trQ/+XdtyMIQkBBbKzX6x97eLfsJQ==", - "dev": true - }, - "node_modules/pofile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.1.tgz", - "integrity": "sha512-RVAzFGo1Mx9+YukVKSgTLut6r4ZVBW8IVrqGHAPfEsVJN93WSp5HRD6+qNa7av1q/joPKDNJd55m5AJl9GBQGA==", - "dev": true - }, - "node_modules/popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" - }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/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/portfinder/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/postcss": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", - "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", - "dev": true, - "dependencies": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", - "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.2" - }, - "peerDependencies": { - "postcss": "^8.0.2" - } - }, - "node_modules/postcss-browser-comments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "browserslist": ">=4", - "postcss": ">=8" - } - }, - "node_modules/postcss-calc": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.2.tgz", - "integrity": "sha512-B5R0UeB4zLJvxNt1FVCaDZULdzsKLPc6FhjFJ+xwFiq7VG4i9cuaJLxVjNtExNK8ocm3n2o4unXXLiVX1SCqxA==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.1.tgz", - "integrity": "sha512-62OBIXCjRXpQZcFOYIXwXBlpAVWrYk8ek1rcjvMING4Q2cf0ipyN9qT+BhHA6HmftGSEnFQu2qgKO3gMscl3Rw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.2.tgz", - "integrity": "sha512-gyx8RgqSmGVK156NAdKcsfkY3KPGHhKqvHTL3hhveFrBBToguKFzhyiuk3cljH6L4fJ0Kv+JENuPXs1Wij27Zw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", - "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-colormin": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.3.tgz", - "integrity": "sha512-dra4xoAjub2wha6RUXAgadHEn2lGxbj8drhFcIGLOMn914Eu7DkPUurugDXgstwttCYkJtZ/+PkWRWdp3UHRIA==", - "dev": true, - "dependencies": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-convert-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz", - "integrity": "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-custom-media": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", - "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-custom-properties": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.3.tgz", - "integrity": "sha512-rtu3otIeY532PnEuuBrIIe+N+pcdbX/7JMZfrcL09wc78YayrHw5E8UkDfvnlOhEUrI4ptCuzXQfj+Or6spbGA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", - "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.2" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.3.tgz", - "integrity": "sha512-qiPm+CNAlgXiMf0J5IbBBEXA9l/Q5HGsNGkL3znIwT2ZFRLGY9U2fTUpa4lqCUXQOxaLimpacHeQC80BD2qbDw==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-discard-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", - "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", - "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-empty": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", - "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.2.tgz", - "integrity": "sha512-+56BLP6NSSUuWUXjRgAQuho1p5xs/hU5Sw7+xt9S3JSg+7R6+WMGnJW7Hre/6tTuZ2xiXMB42ObkiZJ2hy/Pew==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.4.tgz", - "integrity": "sha512-qz+s5vhKJlsHw8HjSs+HVk2QGFdRyC68KGRQGX3i+GcnUjhWhXQEmCXW6siOJkZ1giu0ddPwSO6I6JdVVVPoog==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-env-function": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.4.tgz", - "integrity": "sha512-0ltahRTPtXSIlEZFv7zIvdEib7HN0ZbUQxrxIKn8KbiRyhALo854I/CggU5lyZe6ZBvSTJ6Al2vkZecI2OhneQ==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-flexbugs-fixes": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8.1.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.3.tgz", - "integrity": "sha512-ozOsg+L1U8S+rxSHnJJiET6dNLyADcPHhEarhhtCI9DBLGOPG/2i4ddVoFch9LzrBgb8uDaaRI4nuid2OM82ZA==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-focus-within": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.3.tgz", - "integrity": "sha512-fk9y2uFS6/Kpp7/A9Hz9Z4rlFQ8+tzgBcQCXAFSrXFGAbKx+4ZZOmmfHuYjCOMegPWoz0pnC6fNzi8j7Xyqp5Q==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.2.tgz", - "integrity": "sha512-EaMy/pbxtQnKDsnbEjdqlkCkROTQZzolcLKgIE+3b7EuJfJydH55cZeHfm+MtIezXRqhR80VKgaztO/vHq94Fw==", - "dev": true, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-image-set-function": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.4.tgz", - "integrity": "sha512-BlEo9gSTj66lXjRNByvkMK9dEdEGFXRfGjKRi9fo8s0/P3oEk74cAoonl/utiM50E2OPVb/XSu+lWvdW4KtE/Q==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", - "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", - "dev": true, - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.0.3.tgz", - "integrity": "sha512-MH4tymWmefdZQ7uVG/4icfLjAQmH6o2NRYyVh2mKoB4RXJp9PjsyhZwhH4ouaCQHvg+qJVj3RzeAR1EQpIlXZA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.1.tgz", - "integrity": "sha512-c/9XYboIbSEUZpiD1UQD0IKiUe8n9WHYV7YFe7X7J+ZwCsEKkUJSFWjS9hBU1RR9THR7jMXst8sxiqP0jjo2mg==", - "dev": true, - "dependencies": { - "lilconfig": "^2.0.4", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "dev": true, - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postcss-logical": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.3.tgz", - "integrity": "sha512-P5NcHWYrif0vK8rgOy/T87vg0WRIj3HSknrvp1wzDbiBeoDPVmiVRmkown2eSQdpPveat/MC1ess5uhzZFVnqQ==", - "dev": true, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.4.tgz", - "integrity": "sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.1.0", - "stylehacks": "^5.0.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-merge-rules": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.4.tgz", - "integrity": "sha512-yOj7bW3NxlQxaERBB0lEY1sH5y+RzevjbdH4DBJurjKERNpknRByFNdNe+V72i5pIZL12woM9uGdS5xbSB+kDQ==", - "dev": true, - "dependencies": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.0.0", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.2.tgz", - "integrity": "sha512-R6MJZryq28Cw0AmnyhXrM7naqJZZLoa1paBltIzh2wM7yb4D45TLur+eubTQ4jCmZU9SGeZdWsc5KcSoqTMeTg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.4.tgz", - "integrity": "sha512-RVwZA7NC4R4J76u8X0Q0j+J7ItKUWAeBUJ8oEEZWmtv3Xoh19uNJaJwzNpsydQjk6PkuhRrK+YwwMf+c+68EYg==", - "dev": true, - "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-params": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.3.tgz", - "integrity": "sha512-NY92FUikE+wralaiVexFd5gwb7oJTIDhgTNeIw89i1Ymsgt4RWiPXfz3bg7hDy4NL6gepcThJwOYNtZO/eNi7Q==", - "dev": true, - "dependencies": { - "alphanum-sort": "^1.0.2", - "browserslist": "^4.16.6", - "cssnano-utils": "^3.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.1.tgz", - "integrity": "sha512-TOzqOPXt91O2luJInaVPiivh90a2SIK5Nf1Ea7yEIM/5w+XA5BGrZGUSW8aEx9pJ/oNj7ZJBhjvigSiBV+bC1Q==", - "dev": true, - "dependencies": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", - "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.6" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nesting": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz", - "integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-normalize": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", - "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", - "dev": true, - "dependencies": { - "@csstools/normalize.css": "*", - "postcss-browser-comments": "^4", - "sanitize.css": "*" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "browserslist": ">= 4", - "postcss": ">= 8" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", - "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz", - "integrity": "sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.2.tgz", - "integrity": "sha512-tqghWFVDp2btqFg1gYob1etPNxXLNh3uVeWgZE2AQGh6b2F8AK2Gj36v5Vhyh+APwIzNjmt6jwZ9pTBP+/OM8g==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.2.tgz", - "integrity": "sha512-/rIZn8X9bBzC7KvY4iKUhXUGW3MmbXwfPF23jC9wT9xTi7kAvgj8sEgwxjixBmoL6MVa4WOgxNz2hAR6wTK8tw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-string": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.2.tgz", - "integrity": "sha512-zaI1yzwL+a/FkIzUWMQoH25YwCYxi917J4pYm1nRXtdgiCdnlTkx5eRzqWEC64HtRa06WCJ9TIutpb6GmW4gFw==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz", - "integrity": "sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.2.tgz", - "integrity": "sha512-3y/V+vjZ19HNcTizeqwrbZSUsE69ZMRHfiiyLAJb7C7hJtYmM4Gsbajy7gKagu97E8q5rlS9k8FhojA8cpGhWw==", - "dev": true, - "dependencies": { - "browserslist": "^4.16.6", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-url": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz", - "integrity": "sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg==", - "dev": true, - "dependencies": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.2.tgz", - "integrity": "sha512-CXBx+9fVlzSgbk0IXA/dcZn9lXixnQRndnsPC5ht3HxlQ1bVh77KQDL1GffJx1LTzzfae8ftMulsjYmO2yegxA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-ordered-values": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.3.tgz", - "integrity": "sha512-T9pDS+P9bWeFvqivXd5ACzQmrCmHjv3ZP+djn8E1UZY7iK79pFSm7i3WbKw2VSmFmdbMm8sQ12OPcNpzBo3Z2w==", - "dev": true, - "dependencies": { - "cssnano-utils": "^3.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.2.tgz", - "integrity": "sha512-odBMVt6PTX7jOE9UNvmnLrFzA9pXS44Jd5shFGGtSHY80QCuJF+14McSy0iavZggRZ9Oj//C9vOKQmexvyEJMg==", - "dev": true, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.3.tgz", - "integrity": "sha512-tDQ3m+GYoOar+KoQgj+pwPAvGHAp/Sby6vrFiyrELrMKQJ4AejL0NcS0mm296OKKYA2SRg9ism/hlT/OLhBrdQ==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", - "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", - "dev": true, - "dependencies": { - "autoprefixer": "^10.4.2", - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001299", - "css-blank-pseudo": "^3.0.2", - "css-has-pseudo": "^3.0.3", - "css-prefers-color-scheme": "^6.0.2", - "cssdb": "^5.0.0", - "postcss-attribute-case-insensitive": "^5.0.0", - "postcss-color-functional-notation": "^4.2.1", - "postcss-color-hex-alpha": "^8.0.2", - "postcss-color-rebeccapurple": "^7.0.2", - "postcss-custom-media": "^8.0.0", - "postcss-custom-properties": "^12.1.2", - "postcss-custom-selectors": "^6.0.0", - "postcss-dir-pseudo-class": "^6.0.3", - "postcss-double-position-gradients": "^3.0.4", - "postcss-env-function": "^4.0.4", - "postcss-focus-visible": "^6.0.3", - "postcss-focus-within": "^5.0.3", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.2", - "postcss-image-set-function": "^4.0.4", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.0.3", - "postcss-logical": "^5.0.3", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.2", - "postcss-overflow-shorthand": "^3.0.2", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.3", - "postcss-pseudo-class-any-link": "^7.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^5.0.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.0.2.tgz", - "integrity": "sha512-CG35J1COUH7OOBgpw5O+0koOLUd5N4vUGKUqSAuIe4GiuLHWU96Pqp+UPC8QITTd12zYAFx76pV7qWT/0Aj/TA==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.8" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz", - "integrity": "sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==", - "dev": true, - "dependencies": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.2.tgz", - "integrity": "sha512-25HeDeFsgiPSUx69jJXZn8I06tMxLQJJNF5h7i9gsUg8iP4KOOJ8EX8fj3seeoLt3SLU2YDD6UPnDYVGUO7DEA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", - "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz", - "integrity": "sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", - "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.1.0", - "svgo": "^2.7.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-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==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/postcss-svgo/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==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/postcss-svgo/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==", - "dev": true - }, - "node_modules/postcss-svgo/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==", - "dev": true, - "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" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.2.tgz", - "integrity": "sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA==", - "dev": true, - "dependencies": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "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.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/pretty-format/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/pretty-format/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" - } - }, - "node_modules/pretty-format/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/pretty-format/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/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/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/promise": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", - "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", - "dev": true, - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "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/prop-types-exact": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz", - "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==", - "dev": true, - "dependencies": { - "has": "^1.0.3", - "object.assign": "^4.1.0", - "reflect.ownkeys": "^0.2.0" - } - }, - "node_modules/prop-types-extra": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", - "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", - "dependencies": { - "react-is": "^16.3.2", - "warning": "^4.0.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pseudolocale": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-1.2.0.tgz", - "integrity": "sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==", - "dev": true, - "dependencies": { - "commander": "*" - } - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dev": true, - "dependencies": { - "performance-now": "^2.1.0" - } - }, - "node_modules/railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", - "dev": true - }, - "node_modules/ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, - "node_modules/randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dev": true, - "dependencies": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-ace": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", - "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", - "dependencies": { - "ace-builds": "^1.4.14", - "diff-match-patch": "^1.0.5", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "dev": true, - "dependencies": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-app-polyfill/node_modules/core-js": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/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/react-dev-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-dev-utils/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/react-dev-utils/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/react-dev-utils/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "dev": true, - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/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/react-dev-utils/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/react-dev-utils/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/react-dev-utils/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/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/react-dropzone": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz", - "integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==", - "dependencies": { - "attr-accept": "^1.1.3", - "file-selector": "^0.1.8", - "prop-types": "^15.6.2", - "prop-types-extra": "^1.1.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/react-error-boundary": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", - "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "react": ">=16.13.1" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", - "dev": true - }, - "node_modules/react-fast-compare": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" - }, - "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-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.3.tgz", - "integrity": "sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.3", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-scripts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "bin": { - "react-scripts": "bin/react-scripts.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - }, - "peerDependencies": { - "react": ">= 16", - "typescript": "^3.2.1 || ^4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/react-scripts/node_modules/@types/eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/react-scripts/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-scripts/node_modules/eslint-webpack-plugin": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz", - "integrity": "sha512-xSucskTN9tOkfW7so4EaiFIkulWLXwCB/15H917lR6pTv0Zot6/fetFucmENRb7J5whVSFKIvwnrnsa78SG2yg==", - "dev": true, - "dependencies": { - "@types/eslint": "^7.28.2", - "jest-worker": "^27.3.1", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "schema-utils": "^3.1.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/react-scripts/node_modules/fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/react-scripts/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-shallow-renderer": { - "version": "16.14.1", - "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz", - "integrity": "sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0" - } - }, - "node_modules/react-test-renderer": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", - "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^17.0.2", - "react-shallow-renderer": "^16.13.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/react-test-renderer/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/react-virtualized": { - "version": "9.22.3", - "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz", - "integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==", - "dependencies": { - "@babel/runtime": "^7.7.2", - "clsx": "^1.0.4", - "dom-helpers": "^5.1.3", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-lifecycles-compat": "^3.0.4" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "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.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dev": true, - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reflect.ownkeys": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", - "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=", - "dev": true - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", - "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", - "dev": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", - "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^9.0.0", - "regjsgen": "^0.5.2", - "regjsparser": "^0.7.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "node_modules/regjsparser": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", - "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "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/requireindex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", - "integrity": "sha1-5UBLgVV+91225JxacgBIk/4D4WI=", - "dev": true, - "engines": { - "node": ">=0.10.5" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "node_modules/resolve": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", - "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.8.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-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/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-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "node_modules/resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "dev": true, - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=8.9" - }, - "peerDependencies": { - "rework": "1.0.1", - "rework-visit": "1.0.0" - }, - "peerDependenciesMeta": { - "rework": { - "optional": true - }, - "rework-visit": { - "optional": true - } - } - }, - "node_modules/resolve-url-loader/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true - }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/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" - } - }, - "node_modules/robust-predicates": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", - "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" - }, - "node_modules/rollup": { - "version": "2.64.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.64.0.tgz", - "integrity": "sha512-+c+lbw1lexBKSMb1yxGDVfJ+vchJH3qLbmavR+awDinTDA2C5Ug9u7lkOzj62SCu0PKUExsW36tpgW7Fmpn3yQ==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup-plugin-terser/node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/rollup-plugin-terser/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/rrule": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.7.1.tgz", - "integrity": "sha512-4p20u/1U7WqR3Nb1hOUrm0u1nSI7sO93ZUVZEZ5HeF6Gr5OlJuyhwEGRvUHq8ZfrPsq5gfa5b9dqnUs/kPqpIw==", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/rrule/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "node_modules/rst-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", - "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", - "dev": true, - "dependencies": { - "lodash.flattendeep": "^4.4.0", - "nearley": "^2.7.10" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", - "dev": true - }, - "node_modules/sass-loader": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", - "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", - "dev": true, - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "node_modules/selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", - "dev": true, - "dependencies": { - "node-forge": "^1.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/send/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "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==", - "dev": true - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, - "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/shell-quote": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", - "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "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/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", - "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", - "dev": true, - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/source-map-loader/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", - "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", - "dev": true - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "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_decoder/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 - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz", - "integrity": "sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi/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/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/style-mod": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", - "integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==" - }, - "node_modules/styled-components": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", - "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", - "hasInstallScript": true, - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", - "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/styled-components" - }, - "peerDependencies": { - "react": ">= 16.8.0", - "react-dom": ">= 16.8.0", - "react-is": ">= 16.8.0" - } - }, - "node_modules/stylehacks": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", - "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", - "dev": true, - "dependencies": { - "browserslist": "^4.16.0", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "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/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/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==", - "dev": true, - "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==", - "dev": true - }, - "node_modules/svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svgo/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "node_modules/svgo/node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/svgo/node_modules/dom-serializer/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "node_modules/svgo/node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "node_modules/svgo/node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/svgo/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/svgo/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/svgo/node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "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/tabbable": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", - "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" - }, - "node_modules/tailwindcss": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.15.tgz", - "integrity": "sha512-bT2iy7FtjwgsXik4ZoJnHXR+SRCiGR1W95fVqpLZebr64m4ahwUwRbIAc5w5+2fzr1YF4Ct2eI7dojMRRl8sVQ==", - "dev": true, - "dependencies": { - "arg": "^5.0.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "color-name": "^1.1.4", - "cosmiconfig": "^7.0.1", - "detective": "^5.2.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "normalize-path": "^3.0.0", - "object-hash": "^2.2.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.0", - "postcss-nested": "5.0.6", - "postcss-selector-parser": "^6.0.8", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.21.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "autoprefixer": "^10.0.2", - "postcss": "^8.0.9" - } - }, - "node_modules/tailwindcss/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/tailwindcss/node_modules/arg": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", - "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", - "dev": true - }, - "node_modules/tailwindcss/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/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==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/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/tailwindcss/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/tailwindcss/node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tailwindcss/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/tailwindcss/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/tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", - "dev": true, - "dependencies": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.0.tgz", - "integrity": "sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", - "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", - "dev": true, - "dependencies": { - "jest-worker": "^27.4.1", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.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": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "node_modules/tiny-invariant": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", - "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "node_modules/tippy.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-5.1.2.tgz", - "integrity": "sha512-Qtrv2wqbRbaKMUb6bWWBQWPayvcDKNrGlvihxtsyowhT7RLGEh1STWuy6EMXC6QLkfKPB2MLnf8W2mzql9VDAw==", - "dependencies": { - "popper.js": "^1.16.0" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "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": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "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==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": 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/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "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": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "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" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-keyname": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz", - "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==" - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "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/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/webpack": { - "version": "5.66.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.66.0.tgz", - "integrity": "sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.2" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", - "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.2.2", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/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/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", - "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/serve-index": "^1.9.1", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", - "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "portfinder": "^1.0.28", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", - "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", - "dev": true, - "dependencies": { - "@types/http-proxy": "^1.17.5", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/webpack-dev-server/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/webpack-dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/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/webpack-dev-server/node_modules/ws": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", - "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", - "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/webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "dev": true, - "dependencies": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "webpack": "^4.44.2 || ^5.47.0" - } - }, - "node_modules/webpack-manifest-plugin/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/webpack/node_modules/enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", - "dev": true - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "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/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" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-background-sync": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.4.2.tgz", - "integrity": "sha512-P7c8uG5X2k+DMICH9xeSA9eUlCOjHHYoB42Rq+RtUpuwBxUOflAXR1zdsMWj81LopE4gjKXlTw7BFd1BDAHo7g==", - "dev": true, - "dependencies": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-broadcast-update": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.4.2.tgz", - "integrity": "sha512-qnBwQyE0+PWFFc/n4ISXINE49m44gbEreJUYt2ldGH3+CNrLmJ1egJOOyUqqu9R4Eb7QrXcmB34ClXG7S37LbA==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-build": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.4.2.tgz", - "integrity": "sha512-WMdYLhDIsuzViOTXDH+tJ1GijkFp5khSYolnxR/11zmfhNDtuo7jof72xPGFy+KRpsz6tug39RhivCj77qqO0w==", - "dev": true, - "dependencies": { - "@apideck/better-ajv-errors": "^0.3.1", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "@babel/runtime": "^7.11.2", - "@rollup/plugin-babel": "^5.2.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-replace": "^2.4.1", - "@surma/rollup-plugin-off-main-thread": "^2.2.3", - "ajv": "^8.6.0", - "common-tags": "^1.8.0", - "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "lodash": "^4.17.20", - "pretty-bytes": "^5.3.0", - "rollup": "^2.43.1", - "rollup-plugin-terser": "^7.0.0", - "source-map": "^0.8.0-beta.0", - "source-map-url": "^0.4.0", - "stringify-object": "^3.3.0", - "strip-comments": "^2.0.1", - "tempy": "^0.6.0", - "upath": "^1.2.0", - "workbox-background-sync": "6.4.2", - "workbox-broadcast-update": "6.4.2", - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-google-analytics": "6.4.2", - "workbox-navigation-preload": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-range-requests": "6.4.2", - "workbox-recipes": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2", - "workbox-streams": "6.4.2", - "workbox-sw": "6.4.2", - "workbox-window": "6.4.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.2.tgz", - "integrity": "sha512-JdEazx7qiVqTBzzBl5rolRwl5cmhihjfIcpqRzIZjtT6b18liVmDn/VlWpqW4C/qP2hrFFMLRV1wlex8ZVBPTg==", - "dev": true, - "dependencies": { - "json-schema": "^0.4.0", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "ajv": ">=8" - } - }, - "node_modules/workbox-build/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/workbox-build/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/workbox-build/node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workbox-build/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/workbox-build/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "node_modules/workbox-build/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/workbox-cacheable-response": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.4.2.tgz", - "integrity": "sha512-9FE1W/cKffk1AJzImxgEN0ceWpyz1tqNjZVtA3/LAvYL3AC5SbIkhc7ZCO82WmO9IjTfu8Vut2X/C7ViMSF7TA==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.4.2.tgz", - "integrity": "sha512-1U6cdEYPcajRXiboSlpJx6U7TvhIKbxRRerfepAJu2hniKwJ3DHILjpU/zx3yvzSBCWcNJDoFalf7Vgd7ey/rw==", - "dev": true - }, - "node_modules/workbox-expiration": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.4.2.tgz", - "integrity": "sha512-0hbpBj0tDnW+DZOUmwZqntB/8xrXOgO34i7s00Si/VlFJvvpRKg1leXdHHU8ykoSBd6+F2KDcMP3swoCi5guLw==", - "dev": true, - "dependencies": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-google-analytics": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.4.2.tgz", - "integrity": "sha512-u+gxs3jXovPb1oul4CTBOb+T9fS1oZG+ZE6AzS7l40vnyfJV79DaLBvlpEZfXGv3CjMdV1sT/ltdOrKzo7HcGw==", - "dev": true, - "dependencies": { - "workbox-background-sync": "6.4.2", - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" - } - }, - "node_modules/workbox-navigation-preload": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.4.2.tgz", - "integrity": "sha512-viyejlCtlKsbJCBHwhSBbWc57MwPXvUrc8P7d+87AxBGPU+JuWkT6nvBANgVgFz6FUhCvRC8aYt+B1helo166g==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-precaching": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.4.2.tgz", - "integrity": "sha512-CZ6uwFN/2wb4noHVlALL7UqPFbLfez/9S2GAzGAb0Sk876ul9ukRKPJJ6gtsxfE2HSTwqwuyNVa6xWyeyJ1XSA==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" - } - }, - "node_modules/workbox-range-requests": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.4.2.tgz", - "integrity": "sha512-SowF3z69hr3Po/w7+xarWfzxJX/3Fo0uSG72Zg4g5FWWnHpq2zPvgbWerBZIa81zpJVUdYpMa3akJJsv+LaO1Q==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-recipes": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.4.2.tgz", - "integrity": "sha512-/oVxlZFpAjFVbY+3PoGEXe8qyvtmqMrTdWhbOfbwokNFtUZ/JCtanDKgwDv9x3AebqGAoJRvQNSru0F4nG+gWA==", - "dev": true, - "dependencies": { - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" - } - }, - "node_modules/workbox-routing": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.4.2.tgz", - "integrity": "sha512-0ss/n9PAcHjTy4Ad7l2puuod4WtsnRYu9BrmHcu6Dk4PgWeJo1t5VnGufPxNtcuyPGQ3OdnMdlmhMJ57sSrrSw==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-strategies": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.4.2.tgz", - "integrity": "sha512-YXh9E9dZGEO1EiPC3jPe2CbztO5WT8Ruj8wiYZM56XqEJp5YlGTtqRjghV+JovWOqkWdR+amJpV31KPWQUvn1Q==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2" - } - }, - "node_modules/workbox-streams": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.4.2.tgz", - "integrity": "sha512-ROEGlZHGVEgpa5bOZefiJEVsi5PsFjJG9Xd+wnDbApsCO9xq9rYFopF+IRq9tChyYzhBnyk2hJxbQVWphz3sog==", - "dev": true, - "dependencies": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2" - } - }, - "node_modules/workbox-sw": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.4.2.tgz", - "integrity": "sha512-A2qdu9TLktfIM5NE/8+yYwfWu+JgDaCkbo5ikrky2c7r9v2X6DcJ+zSLphNHHLwM/0eVk5XVf1mC5HGhYpMhhg==", - "dev": true - }, - "node_modules/workbox-webpack-plugin": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.4.2.tgz", - "integrity": "sha512-CiEwM6kaJRkx1cP5xHksn13abTzUqMHiMMlp5Eh/v4wRcedgDTyv6Uo8+Hg9MurRbHDosO5suaPyF9uwVr4/CQ==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "^2.1.0", - "pretty-bytes": "^5.4.1", - "source-map-url": "^0.4.0", - "upath": "^1.2.0", - "webpack-sources": "^1.4.3", - "workbox-build": "6.4.2" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": "^4.4.0 || ^5.9.0" - } - }, - "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/workbox-window": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.4.2.tgz", - "integrity": "sha512-KVyRKmrJg7iB+uym/B/CnEUEFG9CvnTU1Bq5xpXHbtgD9l+ShDekSl1wYpqw/O0JfeeQVOFb8CiNfvnwWwqnWQ==", - "dev": true, - "dependencies": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.4.2" - } - }, - "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-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", - "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", - "dev": true, - "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/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "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/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, - "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": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": 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" - } - } - }, - "dependencies": { - "@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", - "dev": true - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", - "dev": true - }, - "@babel/core": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.10.tgz", - "integrity": "sha512-pbiIdZbCiMx/MM6toR+OfXarYix3uz0oVsnNtfdAGTcCTu3w/JGF8JhirevXLBJUu0WguSZI12qpKnx7EeMyLA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.10", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/eslint-parser": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.5.tgz", - "integrity": "sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==", - "dev": true, - "requires": { - "eslint-scope": "^5.1.1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" - } - }, - "@babel/eslint-plugin": { - "version": "7.16.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-plugin/-/eslint-plugin-7.16.5.tgz", - "integrity": "sha512-R1p6RMyU1Xl1U/NNr+D4+HjkQzN5dQOX0MpjW9WLWhHDjhzN9gso96MxxOFvPh0fKF/mMH8TGW2kuqQ2eK2s9A==", - "dev": true, - "requires": { - "eslint-rule-composer": "^0.3.0" - } - }, - "@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", - "requires": { - "@babel/types": "^7.20.5", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", - "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", - "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", - "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.19.1", - "@babel/helper-split-export-declaration": "^7.18.6" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz", - "integrity": "sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^4.7.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", - "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", - "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", - "dev": true, - "requires": { - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", - "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-wrap-function": "^7.16.8", - "@babel/types": "^7.16.8" - } - }, - "@babel/helper-replace-supers": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", - "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "dev": true, - "requires": { - "@babel/types": "^7.16.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", - "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8" - } - }, - "@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", - "dev": true, - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==" - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.16.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.16.7.tgz", - "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", - "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.7" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", - "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", - "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz", - "integrity": "sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.19.1", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.19.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", - "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", - "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", - "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", - "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", - "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", - "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", - "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", - "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", - "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.7.tgz", - "integrity": "sha512-7twV3pzhrRxSwHeIvFE6coPgvo+exNDOiGUMg39o2LiLo1Y+4aKpfkcLGcg1UHonzorCt7SNXnoMyCnnIOA8Sw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", - "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", - "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", - "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@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==", - "dev": true, - "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==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", - "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", - "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-remap-async-to-generator": "^7.16.8" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", - "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", - "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", - "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", - "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", - "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-flow": "^7.18.6" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", - "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", - "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", - "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", - "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.13.13.tgz", - "integrity": "sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", - "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz", - "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", - "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", - "dev": true, - "requires": { - "@babel/plugin-transform-react-jsx": "^7.16.7" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz", - "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", - "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", - "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", - "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" - }, - "dependencies": { - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", - "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", - "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.2.tgz", - "integrity": "sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.20.2", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", - "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", - "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - } - }, - "@babel/polyfill": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", - "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", - "dev": true, - "requires": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/preset-env": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.10.tgz", - "integrity": "sha512-iCac3fZn9oOcLqc1N2/copPiX7aoxzsvjeDdXoZobrlbQ6YGgS3bL9HyldOJ8V8AY5P7pFynCATrn7M4dMw0Yg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-async-generator-functions": "^7.16.8", - "@babel/plugin-proposal-class-properties": "^7.16.7", - "@babel/plugin-proposal-class-static-block": "^7.16.7", - "@babel/plugin-proposal-dynamic-import": "^7.16.7", - "@babel/plugin-proposal-export-namespace-from": "^7.16.7", - "@babel/plugin-proposal-json-strings": "^7.16.7", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", - "@babel/plugin-proposal-numeric-separator": "^7.16.7", - "@babel/plugin-proposal-object-rest-spread": "^7.16.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", - "@babel/plugin-proposal-optional-chaining": "^7.16.7", - "@babel/plugin-proposal-private-methods": "^7.16.7", - "@babel/plugin-proposal-private-property-in-object": "^7.16.7", - "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", - "@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-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.16.7", - "@babel/plugin-transform-async-to-generator": "^7.16.8", - "@babel/plugin-transform-block-scoped-functions": "^7.16.7", - "@babel/plugin-transform-block-scoping": "^7.16.7", - "@babel/plugin-transform-classes": "^7.16.7", - "@babel/plugin-transform-computed-properties": "^7.16.7", - "@babel/plugin-transform-destructuring": "^7.16.7", - "@babel/plugin-transform-dotall-regex": "^7.16.7", - "@babel/plugin-transform-duplicate-keys": "^7.16.7", - "@babel/plugin-transform-exponentiation-operator": "^7.16.7", - "@babel/plugin-transform-for-of": "^7.16.7", - "@babel/plugin-transform-function-name": "^7.16.7", - "@babel/plugin-transform-literals": "^7.16.7", - "@babel/plugin-transform-member-expression-literals": "^7.16.7", - "@babel/plugin-transform-modules-amd": "^7.16.7", - "@babel/plugin-transform-modules-commonjs": "^7.16.8", - "@babel/plugin-transform-modules-systemjs": "^7.16.7", - "@babel/plugin-transform-modules-umd": "^7.16.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", - "@babel/plugin-transform-new-target": "^7.16.7", - "@babel/plugin-transform-object-super": "^7.16.7", - "@babel/plugin-transform-parameters": "^7.16.7", - "@babel/plugin-transform-property-literals": "^7.16.7", - "@babel/plugin-transform-regenerator": "^7.16.7", - "@babel/plugin-transform-reserved-words": "^7.16.7", - "@babel/plugin-transform-shorthand-properties": "^7.16.7", - "@babel/plugin-transform-spread": "^7.16.7", - "@babel/plugin-transform-sticky-regex": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-transform-typeof-symbol": "^7.16.7", - "@babel/plugin-transform-unicode-escapes": "^7.16.7", - "@babel/plugin-transform-unicode-regex": "^7.16.7", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.16.8", - "babel-plugin-polyfill-corejs2": "^0.3.0", - "babel-plugin-polyfill-corejs3": "^0.5.0", - "babel-plugin-polyfill-regenerator": "^0.3.0", - "core-js-compat": "^3.20.2", - "semver": "^6.3.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", - "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-validator-option": "^7.16.7", - "@babel/plugin-transform-react-display-name": "^7.16.7", - "@babel/plugin-transform-react-jsx": "^7.16.7", - "@babel/plugin-transform-react-jsx-development": "^7.16.7", - "@babel/plugin-transform-react-pure-annotations": "^7.16.7" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/runtime-corejs3": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz", - "integrity": "sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg==", - "dev": true, - "requires": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "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.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.0.2.tgz", - "integrity": "sha512-9PDjnllmXan/7Uax87KGORbxerDJ/cu10SB+n4Jz0zXMEvIh3+TGgZxhIvDOtaQ4jDBQEM7kHYW4vLdQB0DGZQ==", - "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.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.0.1.tgz", - "integrity": "sha512-iNHDByicYqQjs0Wo1MKGfqNbMYMyhS9WV6EwMVwsHXImlFemgEUC+c5X22bXKBStN3qnwg4fArNZM+gkv22baQ==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/language": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.0.tgz", - "integrity": "sha512-tabB0Ef/BflwoEmTB4a//WZ9P90UQyne9qWB9YFsmeS4bnEqSys7UpGk/da1URMXhyfuzWCwp+AQNMhvu8SfnA==", - "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.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.0.0.tgz", - "integrity": "sha512-nUUXcJW1Xp54kNs+a1ToPLK8MadO0rMTnJB8Zk4Z8gBdrN0kqV7uvUraU/T2yqg+grDNR38Vmy/MrhQN/RgwiA==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/search": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.0.0.tgz", - "integrity": "sha512-rL0rd3AhI0TAsaJPUaEwC63KHLO7KL0Z/dYozXj6E7L3wNHRyx7RfE0/j5HsIf912EE5n2PCb4Vg0rGYmDv4UQ==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/state": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.1.0.tgz", - "integrity": "sha512-qbUr94DZTe6/V1VS7LDLz11rM/1t/nJxR1El4I6UaxDEdc0aZZvq6JCLJWiRmUf95NRAnDH6fhXn+PWp9wGCIg==" - }, - "@codemirror/view": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.0.2.tgz", - "integrity": "sha512-mnVT/q1JvKPjpmjXJNeCi/xHyaJ3abGJsumIVpdQ1nE1MXAyHf7GHWt8QpWMUvDiqF0j+inkhVR2OviTdFFX7Q==", - "requires": { - "@codemirror/state": "^6.0.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, - "@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==", - "dev": 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==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==", - "dev": true - }, - "@cypress/instrument-cra": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@cypress/instrument-cra/-/instrument-cra-1.4.0.tgz", - "integrity": "sha512-gXf540xL0jcUXkWyrA2Ug9rzs+jRkc9EPhnRi8XfbnRjdF4lvnn108N6x0lgTApMTbbpCDbVuskHGXDmIuD3CQ==", - "dev": true, - "requires": { - "babel-plugin-istanbul": "6.0.0", - "debug": "4.2.0", - "find-yarn-workspace-root": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - } - } - }, - "@emotion/is-prop-valid": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz", - "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==", - "requires": { - "@emotion/memoize": "^0.7.4" - } - }, - "@emotion/memoize": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", - "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" - }, - "@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" - }, - "@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" - }, - "@eslint/eslintrc": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", - "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.2.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "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 - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", - "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "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 - } - } - }, - "@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/console": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.4.6.tgz", - "integrity": "sha512-jauXyacQD33n47A44KrlOVeiXHEXDqapSdfb9kTekOchH/Pd18kBIO1+xxJQRLuG+LUuljFCwTG92ra4NW7SpA==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/core": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.4.7.tgz", - "integrity": "sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg==", - "dev": true, - "requires": { - "@jest/console": "^27.4.6", - "@jest/reporters": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^27.4.2", - "jest-config": "^27.4.7", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-resolve-dependencies": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "jest-watcher": "^27.4.6", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - } - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/environment": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.4.6.tgz", - "integrity": "sha512-E6t+RXPfATEEGVidr84WngLNWZ8ffCPky8RqqRK6u1Bn0LK92INe0MDttyPl/JOzaq92BmDzOeuqk09TvM22Sg==", - "dev": true, - "requires": { - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/fake-timers": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.6.tgz", - "integrity": "sha512-mfaethuYF8scV8ntPpiVGIHQgS0XIALbpY2jt2l7wb/bvq4Q5pDLk4EP4D7SAvYT1QrPOPVZAtbdGAOOyIgs7A==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/globals": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.4.6.tgz", - "integrity": "sha512-kAiwMGZ7UxrgPzu8Yv9uvWmXXxsy0GciNejlHvfPIfWkSxChzv6bgTS3YqBkGuHcis+ouMFI2696n2t+XYIeFw==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/types": "^27.4.2", - "expect": "^27.4.6" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/reporters": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.6.tgz", - "integrity": "sha512-+Zo9gV81R14+PSq4wzee4GC2mhAN9i9a7qgJWL90Gpx7fHYkWpTBvwWNZUXvJByYR9tAVBdc8VxDWqfJyIUrIQ==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/source-map": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz", - "integrity": "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.6.tgz", - "integrity": "sha512-fi9IGj3fkOrlMmhQqa/t9xum8jaJOOAi/lZlm6JXSc55rJMXKHxNDN1oCP39B0/DhNOa2OMupF9BcKZnNtXMOQ==", - "dev": true, - "requires": { - "@jest/console": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/test-sequencer": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.6.tgz", - "integrity": "sha512-3GL+nsf6E1PsyNsJuvPyIz+DwFuCtBdtvPpm/LMXVkBJbdFvQYCDpccYT56qq5BGniXWlE81n2qk1sdXfZebnw==", - "dev": true, - "requires": { - "@jest/test-result": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-runtime": "^27.4.6" - } - }, - "@jest/transform": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.4.6.tgz", - "integrity": "sha512-9MsufmJC8t5JTpWEQJ0OcOOAXaH5ioaIX6uHVBLBMoCZPfKKQF+EqP8kACAvCZ0Y1h2Zr3uOccg8re+Dr5jxyw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.4.2", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-util": "^27.4.2", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "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.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" - }, - "@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/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@lezer/common": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.0.tgz", - "integrity": "sha512-ohydQe+Hb+w4oMDvXzs8uuJd2NoA3D8YDcLiuDsLqH+yflDTPEpgCsWI3/6rH5C3BAedtH1/R51dxENldQceEA==" - }, - "@lezer/highlight": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.0.0.tgz", - "integrity": "sha512-nsCnNtim90UKsB5YxoX65v3GEIw3iCHw9RM2DtdgkiqAbKh9pCdvi8AWNwkYf10Lu6fxNhXPpkpHbW6mihhvJA==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/lr": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.1.0.tgz", - "integrity": "sha512-Iad04uVwk1PvSnj25mqj7zEEIRAsasbsTRmVzI0AUTs/+1Dz1//iYAaoLr7A+Xa7bZDfql5MKTxZmSlkYZD3Dg==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lingui/babel-plugin-extract-messages": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-3.15.0.tgz", - "integrity": "sha512-iMQmJIkC18Zwc/IDpm3Oclj3KMDQuvipCS2yVHr0MyaeOCeOZ3ZoLVeaa8pfE5pImzlHJ0ss8RRm/St54JElhw==", - "dev": true, - "requires": { - "@babel/generator": "^7.11.6", - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.15.0", - "mkdirp": "^1.0.4" - } - }, - "@lingui/cli": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-3.15.0.tgz", - "integrity": "sha512-6arKc0Mc1z3ABHobjPkhViV+7VUjBhwFwoU0VlT7HBmtrOYad9CBwWEiD+oiEhiHYzLTR7lHTVf674IjTuVvJQ==", - "dev": true, - "requires": { - "@babel/generator": "^7.11.6", - "@babel/parser": "^7.11.5", - "@babel/plugin-syntax-jsx": "^7.10.4", - "@babel/runtime": "^7.11.2", - "@babel/types": "^7.11.5", - "@lingui/babel-plugin-extract-messages": "^3.15.0", - "@lingui/conf": "^3.15.0", - "babel-plugin-macros": "^3.0.1", - "bcp-47": "^1.0.7", - "chalk": "^4.1.0", - "chokidar": "3.5.1", - "cli-table": "0.3.6", - "commander": "^6.1.0", - "date-fns": "^2.16.1", - "fs-extra": "^9.0.1", - "fuzzaldrin": "^2.1.0", - "glob": "^7.1.4", - "inquirer": "^7.3.3", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3", - "micromatch": "4.0.2", - "mkdirp": "^1.0.4", - "node-gettext": "^3.0.0", - "normalize-path": "^3.0.0", - "ora": "^5.1.0", - "papaparse": "^5.3.0", - "pkg-up": "^3.1.0", - "plurals-cldr": "^1.0.4", - "pofile": "^1.1.0", - "pseudolocale": "^1.1.0", - "ramda": "^0.27.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "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 - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@lingui/conf": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-3.15.0.tgz", - "integrity": "sha512-gDGBbqWo6+B3PNjxTGl2asVdd8hC6w+iGsEPonvMw7GFmXb99qybBGdV2ofDlwlT9vChcPwMVtrYE6H0fTZuzA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.11.2", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "cosmiconfig-typescript-loader": "^2.0.1", - "jest-validate": "^26.5.2", - "lodash.get": "^4.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@lingui/core": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/core/-/core-3.14.0.tgz", - "integrity": "sha512-ertREq9oi9B/umxpd/pInm9uFO8FLK2/0FXfDmMqvH5ydswWn/c9nY5YO4W1h4/8LWO45mewypOIyjoue4De1w==", - "requires": { - "@babel/runtime": "^7.11.2", - "make-plural": "^6.2.2", - "messageformat-parser": "^4.1.3" - } - }, - "@lingui/loader": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@lingui/loader/-/loader-3.15.0.tgz", - "integrity": "sha512-Yg7KhinDQmRfqr51bofvD50CuzC1rF8nlFoPsZgLSjpIn4xKdzoCpRaRftRc6sOS5EnoKaK92QwXNv2Ayalz6A==", - "dev": true, - "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/cli": "^3.15.0", - "@lingui/conf": "^3.15.0", - "loader-utils": "^2.0.0" - } - }, - "@lingui/macro": { - "version": "3.8.10", - "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-3.8.10.tgz", - "integrity": "sha512-oZZ/F7HsNQkDsnHFroxzGFuEIXM624H72RIj8j2ClpR64nt+xYDxXYC6TYFicQLtBGcKKBTBoM+zbDaoIv74qQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/conf": "^3.8.10", - "ramda": "^0.27.1" - } - }, - "@lingui/react": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@lingui/react/-/react-3.14.0.tgz", - "integrity": "sha512-ow9Mtru7f0T2S9AwnPWRejppcucCW0LmoDR3P4wqHjL+eH5f8a6nxd2doxGieC91/2i4qqW88y4K/zXJxwRSQw==", - "requires": { - "@babel/runtime": "^7.11.2", - "@lingui/core": "^3.14.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@nteract/mockument": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@nteract/mockument/-/mockument-1.0.4.tgz", - "integrity": "sha1-9/hf2T5Dgo7HQcX0xXMRgu2w7LI=", - "dev": true - }, - "@patternfly/patternfly": { - "version": "4.217.1", - "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.217.1.tgz", - "integrity": "sha512-uN7JgfQsyR16YHkuGRCTIcBcnyKIqKjGkB2SGk9x1XXH3yYGenL83kpAavX9Xtozqp17KppOlybJuzcKvZMrgw==" - }, - "@patternfly/react-core": { - "version": "4.264.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.264.0.tgz", - "integrity": "sha512-tK0BMWxw8nhukev40HZ6q6d02pDnjX7oyA91vHa18aakJUKBWMaerqpG4NZVMoh0tPKX3aLNj+zyCwDALFAZZw==", - "requires": { - "@patternfly/react-icons": "^4.93.0", - "@patternfly/react-styles": "^4.92.0", - "@patternfly/react-tokens": "^4.94.0", - "focus-trap": "6.9.2", - "react-dropzone": "9.0.0", - "tippy.js": "5.1.2", - "tslib": "^2.0.0" - }, - "dependencies": { - "@patternfly/react-icons": { - "version": "4.93.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.93.0.tgz", - "integrity": "sha512-OH0vORVioL+HLWMEog8/3u8jsiMCeJ0pFpvRKRhy5Uk4CdAe40k1SOBvXJP6opr+O8TLbz0q3bm8Jsh/bPaCuQ==", - "requires": {} - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - } - } - }, - "@patternfly/react-icons": { - "version": "4.92.10", - "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.92.10.tgz", - "integrity": "sha512-vwCy7b+OyyuvLDSLqLUG2DkJZgMDogjld8tJTdAaG8HiEhC1sJPZac+5wD7AuS3ym/sQolS4vYtNiVDnMEORxA==", - "requires": {} - }, - "@patternfly/react-styles": { - "version": "4.92.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-4.92.0.tgz", - "integrity": "sha512-B/f6iyu8UEN1+wRxdC4sLIhvJeyL8SqInDXZmwOIqK8uPJ8Lze7qrbVhkkVzbMF37/oDPVa6dZH8qZFq062LEA==" - }, - "@patternfly/react-table": { - "version": "4.108.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-4.108.0.tgz", - "integrity": "sha512-EUvd3rlkE1UXobAm7L6JHgNE3TW8IYTaVwwH/px4Mkn5mBayDO6f+w6QM3OeoDQVZcXK6IYFe7QQaYd/vWIJCQ==", - "requires": { - "@patternfly/react-core": "^4.239.0", - "@patternfly/react-icons": "^4.90.0", - "@patternfly/react-styles": "^4.89.0", - "@patternfly/react-tokens": "^4.91.0", - "lodash": "^4.17.19", - "tslib": "^2.0.0" - }, - "dependencies": { - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - } - } - }, - "@patternfly/react-tokens": { - "version": "4.94.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-4.94.0.tgz", - "integrity": "sha512-fYXxUJZnzpn89K2zzHF0cSncZZVGKrohdb5f5T1wzxwU2NZPVGpvr88xhm+V2Y/fSrrTPwXcP3IIdtNOOtJdZw==" - }, - "@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.4.tgz", - "integrity": "sha512-zZbZeHQDnoTlt2AF+diQT0wsSXpvWiaIOZwBRdltNFhG1+I3ozyaw7U/nBiUwyJ0D+zwdXp0E3bWOl38Ag2BMw==", - "dev": true, - "requires": { - "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", - "core-js-pure": "^3.8.1", - "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "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" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "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": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "@rollup/plugin-babel": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", - "integrity": "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - } - }, - "@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "dependencies": { - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - } - } - }, - "@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - } - } - }, - "@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", - "dev": true, - "requires": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" - } - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "dev": true - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "dev": true - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", - "dev": true - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", - "dev": true - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "dev": true - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", - "dev": true - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", - "dev": true - }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", - "dev": true - }, - "@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "dev": true, - "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - } - }, - "@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "dev": true, - "requires": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - } - } - }, - "@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.6" - } - }, - "@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - } - }, - "@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "dev": true, - "requires": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - }, - "dependencies": { - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - } - } - }, - "@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - } - }, - "@testing-library/dom": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.3.tgz", - "integrity": "sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^4.2.0", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "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": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", - "dev": true, - "requires": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "aria-query": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", - "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/react": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", - "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.0.0", - "@types/react-dom": "<18.0.0" - } - }, - "@testing-library/user-event": { - "version": "14.4.3", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", - "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", - "dev": true, - "requires": {} - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": 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==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/cheerio": { - "version": "0.22.28", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.28.tgz", - "integrity": "sha512-ehUMGSW5IeDxJjbru4awKYMlKGmo1wSSGUVqXtYwlgmUM8X1a0PZttEIm6yEY7vHsY/hh6iPnklF213G0UColw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dev": true, - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/eslint": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.2.tgz", - "integrity": "sha512-nQxgB8/Sg+QKhnV8e0WzPpxjIGT3tuJDDzybkDi8ItE/IgTlHo07U0shaIjzhcvQxlq9SDRE42lsJ23uvEgJ2A==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true - }, - "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.28", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", - "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true - }, - "@types/http-proxy": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz", - "integrity": "sha512-GNkDE7bTv6Sf8JbV2GksknKOsk7OznNYHSdrtvPJXO0qJ9odZig6IZKUi5RFGi6d1bf6dgIAe4uXi3DBc7069Q==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.4.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz", - "integrity": "sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==", - "dev": true, - "requires": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "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": "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": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - } - }, - "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 - } - } - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "@types/node": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz", - "integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/prettier": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.3.tgz", - "integrity": "sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", - "dev": true - }, - "@types/q": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", - "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true - }, - "@types/react": { - "version": "17.0.41", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.41.tgz", - "integrity": "sha512-chYZ9ogWUodyC7VUTRBfblysKLjnohhFY9bGLwvnUFFy48+vB9DikmB3lW0qTFmBcKSzmdglcvkHK71IioOlDA==", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "17.0.14", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.14.tgz", - "integrity": "sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/retry": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", - "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", - "dev": true - }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/testing-library__jest-dom": { - "version": "5.14.3", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz", - "integrity": "sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw==", - "dev": true, - "requires": { - "@types/jest": "*" - } - }, - "@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", - "dev": true - }, - "@types/ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "15.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", - "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", - "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz", - "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.45.0", - "@typescript-eslint/type-utils": "5.45.0", - "@typescript-eslint/utils": "5.45.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.0.tgz", - "integrity": "sha512-DnRQg5+3uHHt/gaifTjwg9OKbg9/TWehfJzYHQIDJboPEbF897BKDE/qoqMhW7nf0jWRV1mwVXTaUvtB1/9Gwg==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.45.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz", - "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.45.0", - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/typescript-estree": "5.45.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz", - "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/visitor-keys": "5.45.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz", - "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.45.0", - "@typescript-eslint/utils": "5.45.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz", - "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz", - "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/visitor-keys": "5.45.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz", - "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.45.0", - "@typescript-eslint/types": "5.45.0", - "@typescript-eslint/typescript-estree": "5.45.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz", - "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.45.0", - "eslint-visitor-keys": "^3.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - } - } - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@wojtekmaj/enzyme-adapter-react-17": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@wojtekmaj/enzyme-adapter-react-17/-/enzyme-adapter-react-17-0.6.5.tgz", - "integrity": "sha512-ChIObUiXXYUiqzXPqOai+p6KF5dlbItpDDYsftUOQiAiygbMDlLeJIjynC6ZrJIa2U2MpRp4YJmtR2GQyIHjgA==", - "dev": true, - "requires": { - "@wojtekmaj/enzyme-adapter-utils": "^0.1.1", - "enzyme-shallow-equal": "^1.0.0", - "has": "^1.0.0", - "object.assign": "^4.1.0", - "object.values": "^1.1.0", - "prop-types": "^15.7.0", - "react-is": "^17.0.2", - "react-test-renderer": "^17.0.0" - }, - "dependencies": { - "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 - } - } - }, - "@wojtekmaj/enzyme-adapter-utils": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@wojtekmaj/enzyme-adapter-utils/-/enzyme-adapter-utils-0.1.1.tgz", - "integrity": "sha512-bNPWtN/d8huKOkC6j1E3EkSamnRrHHT7YuR6f9JppAQqtoAm3v4/vERe4J14jQKmHLCyEBHXrlgb7H6l817hVg==", - "dev": true, - "requires": { - "function.prototype.name": "^1.1.0", - "has": "^1.0.0", - "object.assign": "^4.1.0", - "object.fromentries": "^2.0.0", - "prop-types": "^15.7.0" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "ace-builds": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.10.1.tgz", - "integrity": "sha512-w8Xj6lZUtOYAquVYvdpZhb0GxXrZ+qpVfgj5LP2FwUbXE8fPrCmfu86FjwOiSphx/8PMbXXVldFLD2+RIXayyA==" - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "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-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "address": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", - "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", - "dev": true - }, - "adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - } - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "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" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "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" - } - }, - "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 - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true - }, - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" - }, - "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" - } - }, - "ansi-to-html": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", - "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", - "requires": { - "entities": "^2.2.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "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-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, - "array-filter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", - "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", - "dev": true - }, - "array-find": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", - "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "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.find": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.2.tgz", - "integrity": "sha512-00S1O4ewO95OmmJW7EesWfQlrCrLEL8kZ40w3+GkLX2yTt0m2ggcePPa2uHPJ9KUmJvwRq+lCV9bD8Yim23x/Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - } - }, - "array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - } - }, - "array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - } - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "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 - }, - "attr-accept": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", - "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", - "requires": { - "core-js": "^2.5.0" - } - }, - "autoprefixer": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", - "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", - "dev": true, - "requires": { - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001297", - "fraction.js": "^4.1.2", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "axe-core": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", - "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==", - "dev": true - }, - "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "babel-jest": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.6.tgz", - "integrity": "sha512-qZL0JT0HS1L+lOuH+xC2DVASR3nunZi/ozGhpgauJHgmI7f8rudxf6hUjEHympdQ/J64CdKmPkgfJ+A3U6QCrg==", - "dev": true, - "requires": { - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.4.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", - "dev": true, - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "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" - } - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz", - "integrity": "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - } - }, - "babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "dev": true, - "requires": {} - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz", - "integrity": "sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.20.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", - "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.1" - } - }, - "babel-plugin-styled-components": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz", - "integrity": "sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-module-imports": "^7.0.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "lodash": "^4.17.11" - } - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" - }, - "babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", - "dev": true - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz", - "integrity": "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^27.4.0", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "dev": true, - "requires": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "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 - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcp-47": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", - "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "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" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "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.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" - } - }, - "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-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "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" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true - }, - "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, - "caniuse-api": { - "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==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001434", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", - "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", - "dev": true - }, - "case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "check-types": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", - "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==", - "dev": true - }, - "cheerio": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.9.tgz", - "integrity": "sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng==", - "dev": true, - "requires": { - "cheerio-select": "^1.4.0", - "dom-serializer": "^1.3.1", - "domhandler": "^4.2.0", - "htmlparser2": "^6.1.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", - "dev": true - } - } - }, - "cheerio-select": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.4.0.tgz", - "integrity": "sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==", - "dev": true, - "requires": { - "css-select": "^4.1.2", - "css-what": "^5.0.0", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "ci-info": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", - "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "clean-css": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", - "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "dev": true - }, - "cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", - "dev": true, - "requires": { - "colors": "1.0.3" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - } - }, - "codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "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": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", - "dev": true - }, - "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "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 - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "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 - } - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" - }, - "core-js-compat": { - "version": "3.26.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.1.tgz", - "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", - "dev": true, - "requires": { - "browserslist": "^4.21.4" - } - }, - "core-js-pure": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", - "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cosmiconfig-typescript-loader": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", - "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", - "dev": true, - "requires": { - "cosmiconfig": "^7", - "ts-node": "^10.8.1" - } - }, - "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==", - "dev": true - }, - "crelt": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz", - "integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "css-blank-pseudo": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.2.tgz", - "integrity": "sha512-hOb1LFjRR+8ocA071xUSmg5VslJ8NGo/I2qpUpdeAYyBVCgupS5O8SEVo4SxEMYyFBNodBkzG3T1iqW9HCXxew==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" - }, - "css-declaration-sorter": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz", - "integrity": "sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw==", - "dev": true, - "requires": { - "timsort": "^0.3.0" - } - }, - "css-has-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.3.tgz", - "integrity": "sha512-0gDYWEKaGacwxCqvQ3Ypg6wGdD1AztbMm5h1JsactG2hP2eiflj808QITmuWBpE7sjSEVrAlZhPTVd/nNMj/hQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "css-loader": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", - "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", - "dev": true, - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.2.15", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "dev": true, - "requires": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "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 - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "css-prefers-color-scheme": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.2.tgz", - "integrity": "sha512-gv0KQBEM+q/XdoKyznovq3KW7ocO7k+FhPP+hQR1MenJdu0uPGS6IZa9PzlbqBeS6XcZJNAoqoFxlAUW461CrA==", - "dev": true, - "requires": {} - }, - "css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "css-to-react-native": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", - "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", - "requires": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dev": true, - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - } - }, - "css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", - "dev": true - }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", - "dev": true - }, - "cssdb": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", - "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", - "dev": true - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cssnano": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.15.tgz", - "integrity": "sha512-ppZsS7oPpi2sfiyV5+i+NbB/3GtQ+ab2Vs1azrZaXWujUSN4o+WdTxlCZIMcT9yLW3VO/5yX3vpyDaQ1nIn8CQ==", - "dev": true, - "requires": { - "cssnano-preset-default": "^5.1.10", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - } - }, - "cssnano-preset-default": { - "version": "5.1.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.10.tgz", - "integrity": "sha512-BcpSzUVygHMOnp9uG5rfPzTOCb0GAHQkqtUQx8j1oMNF9A1Q8hziOOhiM4bdICpmrBIU85BE64RD5XGYsVQZNA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^6.0.3", - "cssnano-utils": "^3.0.0", - "postcss-calc": "^8.2.0", - "postcss-colormin": "^5.2.3", - "postcss-convert-values": "^5.0.2", - "postcss-discard-comments": "^5.0.1", - "postcss-discard-duplicates": "^5.0.1", - "postcss-discard-empty": "^5.0.1", - "postcss-discard-overridden": "^5.0.2", - "postcss-merge-longhand": "^5.0.4", - "postcss-merge-rules": "^5.0.4", - "postcss-minify-font-values": "^5.0.2", - "postcss-minify-gradients": "^5.0.4", - "postcss-minify-params": "^5.0.3", - "postcss-minify-selectors": "^5.1.1", - "postcss-normalize-charset": "^5.0.1", - "postcss-normalize-display-values": "^5.0.2", - "postcss-normalize-positions": "^5.0.2", - "postcss-normalize-repeat-style": "^5.0.2", - "postcss-normalize-string": "^5.0.2", - "postcss-normalize-timing-functions": "^5.0.2", - "postcss-normalize-unicode": "^5.0.2", - "postcss-normalize-url": "^5.0.4", - "postcss-normalize-whitespace": "^5.0.2", - "postcss-ordered-values": "^5.0.3", - "postcss-reduce-initial": "^5.0.2", - "postcss-reduce-transforms": "^5.0.2", - "postcss-svgo": "^5.0.3", - "postcss-unique-selectors": "^5.0.2" - } - }, - "cssnano-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.0.tgz", - "integrity": "sha512-Pzs7/BZ6OgT+tXXuF12DKR8SmSbzUeVYCtMBbS8lI0uAm3mrYmkyqCXXPsQESI6kmLfEVBppbdVY/el3hg3nAA==", - "dev": true, - "requires": {} - }, - "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dev": true, - "requires": { - "css-tree": "^1.1.2" - }, - "dependencies": { - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dev": true, - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "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==", - "dev": true - } - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" - }, - "d3": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", - "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", - "requires": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - } - }, - "d3-array": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", - "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", - "requires": { - "internmap": "1 - 2" - } - }, - "d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" - }, - "d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - } - }, - "d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "requires": { - "d3-path": "1 - 3" - } - }, - "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-contour": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", - "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", - "requires": { - "d3-array": "^3.2.0" - } - }, - "d3-delaunay": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", - "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", - "requires": { - "delaunator": "5" - } - }, - "d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" - }, - "d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - } - }, - "d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "requires": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "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==" - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "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-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "requires": { - "d3-dsv": "1 - 3" - } - }, - "d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-format": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.0.1.tgz", - "integrity": "sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==" - }, - "d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", - "requires": { - "d3-array": "2.5.0 - 3" - } - }, - "d3-hierarchy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz", - "integrity": "sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw==" - }, - "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.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==" - }, - "d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" - }, - "d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" - }, - "d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" - }, - "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-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "requires": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - } - }, - "d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" - }, - "d3-shape": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.0.1.tgz", - "integrity": "sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw==", - "requires": { - "d3-path": "1 - 3" - } - }, - "d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-time-format": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.0.0.tgz", - "integrity": "sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==", - "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==" - }, - "d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "requires": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - } - }, - "dagre": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", - "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", - "requires": { - "graphlib": "^2.1.8", - "lodash": "^4.17.15" - } - }, - "damerau-levenshtein": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", - "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==", - "dev": true - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "date-fns": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.21.3.tgz", - "integrity": "sha512-HeYdzCaFflc1i4tGbj7JKMjM4cKGYoyxwcIIkHzNgCkX8xXDNJDZXgDDVchIWpN4eQc3lH37WarduXFZJOtxfw==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", - "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" - }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", - "dev": true, - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, - "delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "requires": { - "robust-predicates": "^3.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "dev": true, - "requires": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "detective": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", - "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", - "dev": true, - "requires": { - "acorn-node": "^1.6.1", - "defined": "^1.0.0", - "minimist": "^1.1.1" - } - }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": 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": "27.4.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", - "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", - "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" - } - }, - "discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", - "dev": true - }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "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.13", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz", - "integrity": "sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==", - "dev": true - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "requires": { - "utila": "~0.4" - } - }, - "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": "1.3.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", - "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", - "dev": true, - "requires": { - "domelementtype": "^2.2.0" - } - }, - "dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==" - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", - "dev": true, - "requires": { - "jake": "^10.8.5" - } - }, - "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true - }, - "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true - }, - "enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - }, - "enzyme": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz", - "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==", - "dev": true, - "requires": { - "array.prototype.flat": "^1.2.3", - "cheerio": "^1.0.0-rc.3", - "enzyme-shallow-equal": "^1.0.1", - "function.prototype.name": "^1.1.2", - "has": "^1.0.3", - "html-element-map": "^1.2.0", - "is-boolean-object": "^1.0.1", - "is-callable": "^1.1.5", - "is-number-object": "^1.0.4", - "is-regex": "^1.0.5", - "is-string": "^1.0.5", - "is-subset": "^0.1.1", - "lodash.escape": "^4.0.1", - "lodash.isequal": "^4.5.0", - "object-inspect": "^1.7.0", - "object-is": "^1.0.2", - "object.assign": "^4.1.0", - "object.entries": "^1.1.1", - "object.values": "^1.1.1", - "raf": "^3.4.1", - "rst-selector-parser": "^2.2.3", - "string.prototype.trim": "^1.2.1" - } - }, - "enzyme-adapter-react-16": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz", - "integrity": "sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==", - "dev": true, - "requires": { - "enzyme-adapter-utils": "^1.14.0", - "enzyme-shallow-equal": "^1.0.4", - "has": "^1.0.3", - "object.assign": "^4.1.2", - "object.values": "^1.1.2", - "prop-types": "^15.7.2", - "react-is": "^16.13.1", - "react-test-renderer": "^16.0.0-0", - "semver": "^5.7.0" - }, - "dependencies": { - "airbnb-prop-types": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz", - "integrity": "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==", - "dev": true, - "requires": { - "array.prototype.find": "^2.1.1", - "function.prototype.name": "^1.1.2", - "is-regex": "^1.1.0", - "object-is": "^1.1.2", - "object.assign": "^4.1.0", - "object.entries": "^1.1.2", - "prop-types": "^15.7.2", - "prop-types-exact": "^1.2.0", - "react-is": "^16.13.1" - } - }, - "enzyme-adapter-utils": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz", - "integrity": "sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg==", - "dev": true, - "requires": { - "airbnb-prop-types": "^2.16.0", - "function.prototype.name": "^1.1.3", - "has": "^1.0.3", - "object.assign": "^4.1.2", - "object.fromentries": "^2.0.3", - "prop-types": "^15.7.2", - "semver": "^5.7.1" - } - }, - "react-test-renderer": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz", - "integrity": "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "react-is": "^16.8.6", - "scheduler": "^0.19.1" - } - }, - "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "enzyme-shallow-equal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz", - "integrity": "sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==", - "dev": true, - "requires": { - "has": "^1.0.3", - "object-is": "^1.1.2" - } - }, - "enzyme-to-json": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.6.2.tgz", - "integrity": "sha512-Ynm6Z6R6iwQ0g2g1YToz6DWhxVnt8Dy1ijR2zynRKxTyBGA8rCDXU3rs2Qc4OKvUvc2Qoe1bcFK6bnPs20TrTg==", - "dev": true, - "requires": { - "@types/cheerio": "^0.22.22", - "lodash": "^4.17.21", - "react-is": "^16.12.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", - "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", - "dev": true, - "requires": { - "stackframe": "^1.1.1" - } - }, - "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - } - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "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" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - } - } - }, - "eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.0.5", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "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.1.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", - "espree": "^9.3.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "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-scope": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", - "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "dev": true - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "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.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "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-config-airbnb": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", - "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", - "dev": true, - "requires": { - "eslint-config-airbnb-base": "^15.0.0", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5" - } - }, - "eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "requires": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "requires": {} - }, - "eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "dev": true, - "requires": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.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" - } - } - } - }, - "eslint-import-resolver-webpack": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.2.tgz", - "integrity": "sha512-XodIPyg1OgE2h5BDErz3WJoK7lawxKTJNhgPNafRST6csC/MZC+L5P6kKqsZGRInpbgc02s/WZMrb4uGJzcuRg==", - "dev": true, - "requires": { - "array-find": "^1.0.0", - "debug": "^3.2.7", - "enhanced-resolve": "^0.9.1", - "find-root": "^1.1.0", - "has": "^1.0.3", - "interpret": "^1.4.0", - "is-core-module": "^2.7.0", - "is-regex": "^1.1.4", - "lodash": "^4.17.21", - "resolve": "^1.20.0", - "semver": "^5.7.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" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", - "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "find-up": "^2.1.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" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "dev": true, - "requires": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - } - }, - "eslint-plugin-i18next": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-i18next/-/eslint-plugin-i18next-5.2.1.tgz", - "integrity": "sha512-yXlWOMiyWz9aCGVrLeFijt+LsCXZj9QoddYXmxUeFZrqst4Z2j6vAMBn2iSE2JTNbPDyrdGl3H03UCo+CbdKbQ==", - "dev": true, - "requires": { - "requireindex": "~1.1.0" - } - }, - "eslint-plugin-import": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", - "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", - "dev": true, - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", - "has": "^1.0.3", - "is-core-module": "^2.8.0", - "is-glob": "^4.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "^5.0.0" - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", - "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", - "dev": true, - "requires": { - "@babel/runtime": "^7.16.3", - "aria-query": "^4.2.2", - "array-includes": "^3.1.4", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.3.5", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.7", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.2.1", - "language-tags": "^1.0.5", - "minimatch": "^3.0.4" - }, - "dependencies": { - "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 - } - } - }, - "eslint-plugin-react": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz", - "integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==", - "dev": true, - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.0", - "object.values": "^1.1.5", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" - }, - "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" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", - "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", - "dev": true, - "requires": {} - }, - "eslint-plugin-testing-library": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", - "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "^5.13.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": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", - "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", - "dev": true, - "requires": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.1.0" - }, - "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", - "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expect": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.6.tgz", - "integrity": "sha512-1M/0kAALIaj5LaG66sFJTbRsWTADnylly82cu4bspI0nl+pgP4E6Bh/aqdHlTUjul06K7xQnnrAoqfxVU0+/ag==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "jest-get-type": "^27.4.0", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dev": true, - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "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 - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "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" - } - }, - "file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - } - }, - "file-selector": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", - "integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==", - "requires": { - "tslib": "^2.0.1" - }, - "dependencies": { - "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - } - } - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "requires": { - "minimatch": "^5.0.1" - }, - "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": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "dependencies": { - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - } - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "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" - } - }, - "find-yarn-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", - "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", - "dev": true, - "requires": { - "micromatch": "^4.0.2" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "focus-trap": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.2.tgz", - "integrity": "sha512-gBEuXOPNOKPrLdZpMFUSTyIo1eT2NSZRrwZ9r/0Jqw5tmT3Yvxfmu8KBHw8xW2XQkw6E/JoG+OlEq7UDtSUNgw==", - "requires": { - "tabbable": "^5.3.2" - } - }, - "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" - }, - "fork-ts-checker-webpack-plugin": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", - "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "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 - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - } - } - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formik": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz", - "integrity": "sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==", - "requires": { - "deepmerge": "^2.1.1", - "hoist-non-react-statics": "^3.3.0", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "react-fast-compare": "^2.0.1", - "tiny-warning": "^1.0.2", - "tslib": "^1.10.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true - }, - "fraction.js": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", - "integrity": "sha512-o2RiJQ6DZaR/5+Si0qJUIy637QMRudSi9kU/FFzx9EZazrIdnBgpU+3sEWCxAVhH2RtxW2Oz+T4p2o8uOPVcgA==", - "dev": true - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true - }, - "fs-extra": { - "version": "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-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "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 - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.4.tgz", - "integrity": "sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "functions-have-names": "^1.2.2" - } - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "functions-have-names": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz", - "integrity": "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==", - "dev": true - }, - "fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=", - "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==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "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" - } - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", - "dev": true - }, - "graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", - "requires": { - "lodash": "^4.17.15" - } - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dev": true, - "requires": { - "duplexer": "^0.1.2" - } - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-5.0.1.tgz", - "integrity": "sha512-Fp2IsZDnnyoJkKg22ZyQFvD7QRCcMTsLAtloKXyXWJ1joGLtItRU9Bv/k1o0tELL2NF3ZZBcycSKryZUM+Yl3g==", - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, - "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" - } - }, - "hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-element-map": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.0.tgz", - "integrity": "sha512-AqCt/m9YaiMwaaAyOPdq4Ga0cM+jdDWWGueUMkdROZcTeClaGpN0AQeyGchZhTegQoABmc6+IqH7oCR/8vhQYg==", - "dev": true, - "requires": { - "array-filter": "^1.0.0", - "call-bind": "^1.0.2" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==" - }, - "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-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "requires": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "dependencies": { - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true - } - } - }, - "html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "dev": true, - "requires": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "dependencies": { - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - } - } - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } - } - }, - "http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "http-proxy-middleware": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", - "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", - "dev": true, - "requires": { - "@types/http-proxy": "^1.17.5", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "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" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} - }, - "idb": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", - "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==", - "dev": true - }, - "identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=", - "dev": true, - "requires": { - "harmony-reflect": "^1.4.6" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "immer": { - "version": "9.0.16", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", - "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "dependencies": { - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "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 - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "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": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "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==" - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "dev": true - }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.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-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", - "dev": true - }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "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==", - "dev": true - }, - "is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true - }, - "is-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-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-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", - "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", - "dev": true - }, - "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-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, - "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-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-weakref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", - "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dev": true, - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.7.tgz", - "integrity": "sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==", - "dev": true, - "requires": { - "@jest/core": "^27.4.7", - "import-local": "^3.0.2", - "jest-cli": "^27.4.7" - } - }, - "jest-changed-files": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz", - "integrity": "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-circus": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.6.tgz", - "integrity": "sha512-UA7AI5HZrW4wRM72Ro80uRR2Fg+7nR0GESbSI/2M+ambbzVuA63mn5T1p3Z/wlhntzGpIG1xx78GP2YIkf6PhQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-cli": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.7.tgz", - "integrity": "sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw==", - "dev": true, - "requires": { - "@jest/core": "^27.4.7", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "jest-config": "^27.4.7", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - } - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-config": { - "version": "27.4.7", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.7.tgz", - "integrity": "sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw==", - "dev": true, - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.4.6", - "@jest/types": "^27.4.2", - "babel-jest": "^27.4.6", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-circus": "^27.4.6", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-jasmine2": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-runner": "^27.4.6", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "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 - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - } - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-diff": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.6.tgz", - "integrity": "sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.4.0", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-docblock": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz", - "integrity": "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.4.6.tgz", - "integrity": "sha512-n6QDq8y2Hsmn22tRkgAk+z6MCX7MeVlAzxmZDshfS2jLcaBlyhpF3tZSJLR+kXmh23GEvS0ojMR8i6ZeRvpQcA==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-jsdom": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.6.tgz", - "integrity": "sha512-o3dx5p/kHPbUlRvSNjypEcEtgs6LmvESMzgRFQE6c+Prwl2JLA4RZ7qAnxc5VM8kutsGRTB15jXeeSbJsKN9iA==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2", - "jsdom": "^16.6.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-node": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.6.tgz", - "integrity": "sha512-yfHlZ9m+kzTKZV0hVfhVu6GuDxKAYeFHrfulmy7Jxwsq4V7+ZK7f+c0XP/tbVDMQW7E4neG2u147hFkuVz0MlQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "jest-mock": "^27.4.6", - "jest-util": "^27.4.2" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-haste-map": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.6.tgz", - "integrity": "sha512-0tNpgxg7BKurZeFkIOvGCkbmOHbLFf4LUQOxrQSMjvrQaQe3l6E8x6jYC1NuWkGo5WDdbr8FEzUxV2+LWNawKQ==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^27.4.0", - "jest-serializer": "^27.4.0", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-jasmine2": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.6.tgz", - "integrity": "sha512-uAGNXF644I/whzhsf7/qf74gqy9OuhvJ0XYp8SDecX2ooGeaPnmJMjXjKt0mqh1Rl5dtRGxJgNrHlBQIBfS5Nw==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.4.6", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "pretty-format": "^27.4.6", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-leak-detector": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.6.tgz", - "integrity": "sha512-kkaGixDf9R7CjHm2pOzfTxZTQQQ2gHTIWKY/JZSiYTc90bZp8kSZnUMS3uLAfwTZwc0tcMRoEX74e14LG1WapA==", - "dev": true, - "requires": { - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - } - }, - "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 - } - } - }, - "jest-matcher-utils": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.6.tgz", - "integrity": "sha512-XD4PKT3Wn1LQnRAq7ZsTI0VRuEc9OrCPFiO1XL7bftTGmfNF0DcEwMHRgqiu7NGf8ZoZDREpGrCniDkjt79WbA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "pretty-format": "^27.4.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-message-util": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.6.tgz", - "integrity": "sha512-0p5szriFU0U74czRSFjH6RyS7UYIAkn/ntwMuOwTGWrQIOh5NzXXrq72LOqIkJKKvFbPq+byZKuBz78fjBERBA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.4.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "pretty-format": "^27.4.6", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-mock": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.6.tgz", - "integrity": "sha512-kvojdYRkst8iVSZ1EJ+vc1RRD9llueBjKzXzeCytH3dMM7zvPV/ULcfI2nr0v0VUgm3Bjt3hBCQvOeaBz+ZTHw==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "@types/node": "*" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz", - "integrity": "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==", - "dev": true - }, - "jest-resolve": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.6.tgz", - "integrity": "sha512-SFfITVApqtirbITKFAO7jOVN45UgFzcRdQanOFzjnbd+CACDoyeX7206JyU92l4cRr73+Qy/TlW51+4vHGt+zw==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.4.2", - "jest-validate": "^27.4.6", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "jest-validate": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.6.tgz", - "integrity": "sha512-872mEmCPVlBqbA5dToC57vA3yJaMRfIdpCoD3cyHWJOMx+SJwLNw0I71EkWs41oza/Er9Zno9XuTkRYCPDUJXQ==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.4.0", - "leven": "^3.1.0", - "pretty-format": "^27.4.6" - } - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.6.tgz", - "integrity": "sha512-W85uJZcFXEVZ7+MZqIPCscdjuctruNGXUZ3OHSXOfXR9ITgbUKeHj+uGcies+0SsvI5GtUfTw4dY7u9qjTvQOw==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "jest-regex-util": "^27.4.0", - "jest-snapshot": "^27.4.6" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runner": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.6.tgz", - "integrity": "sha512-IDeFt2SG4DzqalYBZRgbbPmpwV3X0DcntjezPBERvnhwKGWTW7C5pbbA5lVkmvgteeNfdd/23gwqv3aiilpYPg==", - "dev": true, - "requires": { - "@jest/console": "^27.4.6", - "@jest/environment": "^27.4.6", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-docblock": "^27.4.0", - "jest-environment-jsdom": "^27.4.6", - "jest-environment-node": "^27.4.6", - "jest-haste-map": "^27.4.6", - "jest-leak-detector": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-resolve": "^27.4.6", - "jest-runtime": "^27.4.6", - "jest-util": "^27.4.2", - "jest-worker": "^27.4.6", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runtime": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.6.tgz", - "integrity": "sha512-eXYeoR/MbIpVDrjqy5d6cGCFOYBFFDeKaNWqTp0h6E74dK0zLHzASQXJpl5a2/40euBmKnprNLJ0Kh0LCndnWQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.4.6", - "@jest/fake-timers": "^27.4.6", - "@jest/globals": "^27.4.6", - "@jest/source-map": "^27.4.0", - "@jest/test-result": "^27.4.6", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-mock": "^27.4.6", - "jest-regex-util": "^27.4.0", - "jest-resolve": "^27.4.6", - "jest-snapshot": "^27.4.6", - "jest-util": "^27.4.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-serializer": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", - "integrity": "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.6.tgz", - "integrity": "sha512-fafUCDLQfzuNP9IRcEqaFAMzEe7u5BF7mude51wyWv7VRex60WznZIC7DfKTgSIlJa8aFzYmXclmN328aqSDmQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.4.6", - "graceful-fs": "^4.2.4", - "jest-diff": "^27.4.6", - "jest-get-type": "^27.4.0", - "jest-haste-map": "^27.4.6", - "jest-matcher-utils": "^27.4.6", - "jest-message-util": "^27.4.6", - "jest-util": "^27.4.2", - "natural-compare": "^1.4.0", - "pretty-format": "^27.4.6", - "semver": "^7.3.2" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", - "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", - "dev": true - }, - "pretty-format": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.6.tgz", - "integrity": "sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g==", - "dev": true, - "requires": { - "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 - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-util": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz", - "integrity": "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==", - "dev": true, - "requires": { - "@jest/types": "^27.4.2", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.4", - "picomatch": "^2.2.3" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watch-typeahead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.0.0.tgz", - "integrity": "sha512-jxoszalAb394WElmiJTFBMzie/RDCF+W7Q29n5LzOPtcoQoHWfdUtHFkbhgf5NwWe8uMOxvKb/g7ea7CshfkTw==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^27.0.0", - "jest-watcher": "^27.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.0.tgz", - "integrity": "sha512-oGu2QekBMXgyQNWPDRQ001bjvDnZe4/zBTz37TMbiKz1NbNiyiH5hRkobe7npRN6GfbGbxMYFck/vQ1r9c1VMA==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - }, - "string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "dev": true, - "requires": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - } - }, - "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, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watcher": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.6.tgz", - "integrity": "sha512-yKQ20OMBiCDigbD0quhQKLkBO+ObGN79MO4nT7YaCuQ5SM+dkBNWE8cZX0FjU6czwMvWw6StWbe+Wv4jJPJ+fw==", - "dev": true, - "requires": { - "@jest/test-result": "^27.4.6", - "@jest/types": "^27.4.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.4.2", - "string-length": "^4.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", - "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-websocket-mock": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jest-websocket-mock/-/jest-websocket-mock-2.2.0.tgz", - "integrity": "sha512-lc3wwXOEyNa4ZpcgJtUG3mmKMAq5FAsKYiZph0p/+PAJrAPuX4JCIfJMdJ/urRsLBG51fwm/wlVPNbR6s2nzNw==", - "dev": true - }, - "jest-worker": { - "version": "27.4.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", - "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "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" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "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-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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "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" - } - }, - "jsonpointer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.0.tgz", - "integrity": "sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==", - "dev": true - }, - "jsx-ast-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", - "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "object.assign": "^4.1.2" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", - "dev": true - }, - "language-subtag-registry": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", - "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", - "dev": true - }, - "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", - "dev": true, - "requires": { - "language-subtag-registry": "~0.3.2" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "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.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", - "dev": true - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "dev": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.escape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", - "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "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": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "requires": { - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "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" - } - }, - "luxon": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", - "integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==" - }, - "lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", - "dev": true - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-plural": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-6.2.2.tgz", - "integrity": "sha512-8iTuFioatnTTmb/YJjywkVIHLjcwkFD9Ms0JpxjEm9Mo8eQYkh1z+55dwv4yc1jQ8ftVBxWQbihvZL1DfzGGWA==" - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true - }, - "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==", - "dev": true - }, - "memfs": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", - "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", - "dev": true, - "requires": { - "fs-monkey": "1.0.3" - } - }, - "memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", - "dev": true - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "messageformat-parser": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-4.1.3.tgz", - "integrity": "sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - } - }, - "mini-css-extract-plugin": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.2.tgz", - "integrity": "sha512-Lwgq9qLNyBK6yNLgzssXnq4r2+mB9Mz3cJWlM8kseysHIvTicFhDNimFgY94jjqlwhNzLPsq8wv4X+vOHtMdYA==", - "dev": true, - "requires": { - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "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 - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "mock-socket": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.1.3.tgz", - "integrity": "sha512-uz8lx8c5wuJYJ21f5UtovqpV0+KJuVwE7cVOLNhrl2QW/CvmstOLRfjXnLSbfFHZtJtiaSGQu0oCJA8SmRcK6A==", - "dev": true - }, - "moo": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", - "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "nearley": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", - "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "moo": "^0.5.0", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6" - } - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true - }, - "node-gettext": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-3.0.0.tgz", - "integrity": "sha512-/VRYibXmVoN6tnSAY2JWhNRhWYJ8Cd844jrZU/DwLVoI4vBI6ceYbd8i42sYZ9uOgDH3S7vslIKOWV/ZrT2YBA==", - "dev": true, - "requires": { - "lodash.get": "^4.4.2" - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true - }, - "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 - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", - "dev": true, - "requires": { - "boolbase": "^1.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "dev": true - }, - "object-inspect": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz", - "integrity": "sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA==", - "dev": true - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "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 - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", - "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2" - } - }, - "object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-retry": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", - "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.13.1" - } - }, - "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 - }, - "papaparse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.3.0.tgz", - "integrity": "sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg==", - "dev": true - }, - "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": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "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": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - }, - "dependencies": { - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - } - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true - }, - "pirates": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", - "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", - "dev": true - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "plurals-cldr": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/plurals-cldr/-/plurals-cldr-1.0.4.tgz", - "integrity": "sha512-4nLXqtel7fsCgzi8dvRZvUjfL8SXpP982sKg7b2TgpnR8rDnes06iuQ83trQ/+XdtyMIQkBBbKzX6x97eLfsJQ==", - "dev": true - }, - "pofile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.1.1.tgz", - "integrity": "sha512-RVAzFGo1Mx9+YukVKSgTLut6r4ZVBW8IVrqGHAPfEsVJN93WSp5HRD6+qNa7av1q/joPKDNJd55m5AJl9GBQGA==", - "dev": true - }, - "popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "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" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "postcss": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", - "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", - "dev": true, - "requires": { - "nanoid": "^3.1.30", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.1" - } - }, - "postcss-attribute-case-insensitive": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", - "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.2" - } - }, - "postcss-browser-comments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "dev": true, - "requires": {} - }, - "postcss-calc": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.2.tgz", - "integrity": "sha512-B5R0UeB4zLJvxNt1FVCaDZULdzsKLPc6FhjFJ+xwFiq7VG4i9cuaJLxVjNtExNK8ocm3n2o4unXXLiVX1SCqxA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - } - }, - "postcss-color-functional-notation": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.1.tgz", - "integrity": "sha512-62OBIXCjRXpQZcFOYIXwXBlpAVWrYk8ek1rcjvMING4Q2cf0ipyN9qT+BhHA6HmftGSEnFQu2qgKO3gMscl3Rw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-hex-alpha": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.2.tgz", - "integrity": "sha512-gyx8RgqSmGVK156NAdKcsfkY3KPGHhKqvHTL3hhveFrBBToguKFzhyiuk3cljH6L4fJ0Kv+JENuPXs1Wij27Zw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-rebeccapurple": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", - "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-colormin": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.3.tgz", - "integrity": "sha512-dra4xoAjub2wha6RUXAgadHEn2lGxbj8drhFcIGLOMn914Eu7DkPUurugDXgstwttCYkJtZ/+PkWRWdp3UHRIA==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-convert-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz", - "integrity": "sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-custom-media": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", - "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true, - "requires": {} - }, - "postcss-custom-properties": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.3.tgz", - "integrity": "sha512-rtu3otIeY532PnEuuBrIIe+N+pcdbX/7JMZfrcL09wc78YayrHw5E8UkDfvnlOhEUrI4ptCuzXQfj+Or6spbGA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-custom-selectors": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", - "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-dir-pseudo-class": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.3.tgz", - "integrity": "sha512-qiPm+CNAlgXiMf0J5IbBBEXA9l/Q5HGsNGkL3znIwT2ZFRLGY9U2fTUpa4lqCUXQOxaLimpacHeQC80BD2qbDw==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "postcss-discard-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", - "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", - "dev": true, - "requires": {} - }, - "postcss-discard-duplicates": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", - "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", - "dev": true, - "requires": {} - }, - "postcss-discard-empty": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", - "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", - "dev": true, - "requires": {} - }, - "postcss-discard-overridden": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.2.tgz", - "integrity": "sha512-+56BLP6NSSUuWUXjRgAQuho1p5xs/hU5Sw7+xt9S3JSg+7R6+WMGnJW7Hre/6tTuZ2xiXMB42ObkiZJ2hy/Pew==", - "dev": true, - "requires": {} - }, - "postcss-double-position-gradients": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.0.4.tgz", - "integrity": "sha512-qz+s5vhKJlsHw8HjSs+HVk2QGFdRyC68KGRQGX3i+GcnUjhWhXQEmCXW6siOJkZ1giu0ddPwSO6I6JdVVVPoog==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-env-function": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.4.tgz", - "integrity": "sha512-0ltahRTPtXSIlEZFv7zIvdEib7HN0ZbUQxrxIKn8KbiRyhALo854I/CggU5lyZe6ZBvSTJ6Al2vkZecI2OhneQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-flexbugs-fixes": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "dev": true, - "requires": {} - }, - "postcss-focus-visible": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.3.tgz", - "integrity": "sha512-ozOsg+L1U8S+rxSHnJJiET6dNLyADcPHhEarhhtCI9DBLGOPG/2i4ddVoFch9LzrBgb8uDaaRI4nuid2OM82ZA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "postcss-focus-within": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.3.tgz", - "integrity": "sha512-fk9y2uFS6/Kpp7/A9Hz9Z4rlFQ8+tzgBcQCXAFSrXFGAbKx+4ZZOmmfHuYjCOMegPWoz0pnC6fNzi8j7Xyqp5Q==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "requires": {} - }, - "postcss-gap-properties": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.2.tgz", - "integrity": "sha512-EaMy/pbxtQnKDsnbEjdqlkCkROTQZzolcLKgIE+3b7EuJfJydH55cZeHfm+MtIezXRqhR80VKgaztO/vHq94Fw==", - "dev": true, - "requires": {} - }, - "postcss-image-set-function": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.4.tgz", - "integrity": "sha512-BlEo9gSTj66lXjRNByvkMK9dEdEGFXRfGjKRi9fo8s0/P3oEk74cAoonl/utiM50E2OPVb/XSu+lWvdW4KtE/Q==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "requires": {} - }, - "postcss-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", - "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", - "dev": true, - "requires": { - "camelcase-css": "^2.0.1" - } - }, - "postcss-lab-function": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.0.3.tgz", - "integrity": "sha512-MH4tymWmefdZQ7uVG/4icfLjAQmH6o2NRYyVh2mKoB4RXJp9PjsyhZwhH4ouaCQHvg+qJVj3RzeAR1EQpIlXZA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-load-config": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.1.tgz", - "integrity": "sha512-c/9XYboIbSEUZpiD1UQD0IKiUe8n9WHYV7YFe7X7J+ZwCsEKkUJSFWjS9hBU1RR9THR7jMXst8sxiqP0jjo2mg==", - "dev": true, - "requires": { - "lilconfig": "^2.0.4", - "yaml": "^1.10.2" - } - }, - "postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "dev": true, - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "postcss-logical": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.3.tgz", - "integrity": "sha512-P5NcHWYrif0vK8rgOy/T87vg0WRIj3HSknrvp1wzDbiBeoDPVmiVRmkown2eSQdpPveat/MC1ess5uhzZFVnqQ==", - "dev": true, - "requires": {} - }, - "postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "requires": {} - }, - "postcss-merge-longhand": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.4.tgz", - "integrity": "sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.1.0", - "stylehacks": "^5.0.1" - } - }, - "postcss-merge-rules": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.4.tgz", - "integrity": "sha512-yOj7bW3NxlQxaERBB0lEY1sH5y+RzevjbdH4DBJurjKERNpknRByFNdNe+V72i5pIZL12woM9uGdS5xbSB+kDQ==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.0.0", - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-minify-font-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.2.tgz", - "integrity": "sha512-R6MJZryq28Cw0AmnyhXrM7naqJZZLoa1paBltIzh2wM7yb4D45TLur+eubTQ4jCmZU9SGeZdWsc5KcSoqTMeTg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-gradients": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.4.tgz", - "integrity": "sha512-RVwZA7NC4R4J76u8X0Q0j+J7ItKUWAeBUJ8oEEZWmtv3Xoh19uNJaJwzNpsydQjk6PkuhRrK+YwwMf+c+68EYg==", - "dev": true, - "requires": { - "colord": "^2.9.1", - "cssnano-utils": "^3.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-params": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.3.tgz", - "integrity": "sha512-NY92FUikE+wralaiVexFd5gwb7oJTIDhgTNeIw89i1Ymsgt4RWiPXfz3bg7hDy4NL6gepcThJwOYNtZO/eNi7Q==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.2", - "browserslist": "^4.16.6", - "cssnano-utils": "^3.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.1.tgz", - "integrity": "sha512-TOzqOPXt91O2luJInaVPiivh90a2SIK5Nf1Ea7yEIM/5w+XA5BGrZGUSW8aEx9pJ/oNj7ZJBhjvigSiBV+bC1Q==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} - }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-nested": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", - "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.6" - } - }, - "postcss-nesting": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.2.tgz", - "integrity": "sha512-dJGmgmsvpzKoVMtDMQQG/T6FSqs6kDtUDirIfl4KnjMCiY9/ETX8jdKyCd20swSRAbUYkaBKV20pxkzxoOXLqQ==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "postcss-normalize": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", - "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", - "dev": true, - "requires": { - "@csstools/normalize.css": "*", - "postcss-browser-comments": "^4", - "sanitize.css": "*" - } - }, - "postcss-normalize-charset": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", - "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", - "dev": true, - "requires": {} - }, - "postcss-normalize-display-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz", - "integrity": "sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-positions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.2.tgz", - "integrity": "sha512-tqghWFVDp2btqFg1gYob1etPNxXLNh3uVeWgZE2AQGh6b2F8AK2Gj36v5Vhyh+APwIzNjmt6jwZ9pTBP+/OM8g==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.2.tgz", - "integrity": "sha512-/rIZn8X9bBzC7KvY4iKUhXUGW3MmbXwfPF23jC9wT9xTi7kAvgj8sEgwxjixBmoL6MVa4WOgxNz2hAR6wTK8tw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-string": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.2.tgz", - "integrity": "sha512-zaI1yzwL+a/FkIzUWMQoH25YwCYxi917J4pYm1nRXtdgiCdnlTkx5eRzqWEC64HtRa06WCJ9TIutpb6GmW4gFw==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz", - "integrity": "sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-unicode": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.2.tgz", - "integrity": "sha512-3y/V+vjZ19HNcTizeqwrbZSUsE69ZMRHfiiyLAJb7C7hJtYmM4Gsbajy7gKagu97E8q5rlS9k8FhojA8cpGhWw==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-url": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz", - "integrity": "sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg==", - "dev": true, - "requires": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-whitespace": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.2.tgz", - "integrity": "sha512-CXBx+9fVlzSgbk0IXA/dcZn9lXixnQRndnsPC5ht3HxlQ1bVh77KQDL1GffJx1LTzzfae8ftMulsjYmO2yegxA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-ordered-values": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.3.tgz", - "integrity": "sha512-T9pDS+P9bWeFvqivXd5ACzQmrCmHjv3ZP+djn8E1UZY7iK79pFSm7i3WbKw2VSmFmdbMm8sQ12OPcNpzBo3Z2w==", - "dev": true, - "requires": { - "cssnano-utils": "^3.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-overflow-shorthand": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.2.tgz", - "integrity": "sha512-odBMVt6PTX7jOE9UNvmnLrFzA9pXS44Jd5shFGGtSHY80QCuJF+14McSy0iavZggRZ9Oj//C9vOKQmexvyEJMg==", - "dev": true, - "requires": {} - }, - "postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "requires": {} - }, - "postcss-place": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.3.tgz", - "integrity": "sha512-tDQ3m+GYoOar+KoQgj+pwPAvGHAp/Sby6vrFiyrELrMKQJ4AejL0NcS0mm296OKKYA2SRg9ism/hlT/OLhBrdQ==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-preset-env": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", - "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", - "dev": true, - "requires": { - "autoprefixer": "^10.4.2", - "browserslist": "^4.19.1", - "caniuse-lite": "^1.0.30001299", - "css-blank-pseudo": "^3.0.2", - "css-has-pseudo": "^3.0.3", - "css-prefers-color-scheme": "^6.0.2", - "cssdb": "^5.0.0", - "postcss-attribute-case-insensitive": "^5.0.0", - "postcss-color-functional-notation": "^4.2.1", - "postcss-color-hex-alpha": "^8.0.2", - "postcss-color-rebeccapurple": "^7.0.2", - "postcss-custom-media": "^8.0.0", - "postcss-custom-properties": "^12.1.2", - "postcss-custom-selectors": "^6.0.0", - "postcss-dir-pseudo-class": "^6.0.3", - "postcss-double-position-gradients": "^3.0.4", - "postcss-env-function": "^4.0.4", - "postcss-focus-visible": "^6.0.3", - "postcss-focus-within": "^5.0.3", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.2", - "postcss-image-set-function": "^4.0.4", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.0.3", - "postcss-logical": "^5.0.3", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.2", - "postcss-overflow-shorthand": "^3.0.2", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.3", - "postcss-pseudo-class-any-link": "^7.0.2", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^5.0.0" - } - }, - "postcss-pseudo-class-any-link": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.0.2.tgz", - "integrity": "sha512-CG35J1COUH7OOBgpw5O+0koOLUd5N4vUGKUqSAuIe4GiuLHWU96Pqp+UPC8QITTd12zYAFx76pV7qWT/0Aj/TA==", - "dev": true, - "requires": { - "postcss-selector-parser": "^6.0.8" - } - }, - "postcss-reduce-initial": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz", - "integrity": "sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==", - "dev": true, - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.2.tgz", - "integrity": "sha512-25HeDeFsgiPSUx69jJXZn8I06tMxLQJJNF5h7i9gsUg8iP4KOOJ8EX8fj3seeoLt3SLU2YDD6UPnDYVGUO7DEA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "requires": {} - }, - "postcss-selector-not": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", - "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "postcss-selector-parser": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz", - "integrity": "sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-svgo": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", - "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", - "dev": true, - "requires": { - "postcss-value-parser": "^4.1.0", - "svgo": "^2.7.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==", - "dev": true - }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dev": true, - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "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==", - "dev": true - }, - "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dev": true, - "requires": { - "@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" - } - } - } - }, - "postcss-unique-selectors": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.2.tgz", - "integrity": "sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5" - } - }, - "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.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", - "dev": true - }, - "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 - }, - "pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "requires": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "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 - } - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "promise": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", - "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", - "dev": true, - "requires": { - "asap": "~2.0.6" - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "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": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "prop-types-exact": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz", - "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==", - "dev": true, - "requires": { - "has": "^1.0.3", - "object.assign": "^4.1.0", - "reflect.ownkeys": "^0.2.0" - } - }, - "prop-types-extra": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", - "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", - "requires": { - "react-is": "^16.3.2", - "warning": "^4.0.0" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - } - } - }, - "pseudolocale": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pseudolocale/-/pseudolocale-1.2.0.tgz", - "integrity": "sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==", - "dev": true, - "requires": { - "commander": "*" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, - "raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dev": true, - "requires": { - "performance-now": "^2.1.0" - } - }, - "railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", - "dev": true - }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, - "randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dev": true, - "requires": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true - } - } - }, - "react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "react-ace": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-10.1.0.tgz", - "integrity": "sha512-VkvUjZNhdYTuKOKQpMIZi7uzZZVgzCjM7cLYu6F64V0mejY8a2XTyPUIMszC6A4trbeMIHbK5fYFcT/wkP/8VA==", - "requires": { - "ace-builds": "^1.4.14", - "diff-match-patch": "^1.0.5", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "prop-types": "^15.7.2" - } - }, - "react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "dev": true, - "requires": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - }, - "dependencies": { - "core-js": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", - "dev": true - } - } - }, - "react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "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 - }, - "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" - } - }, - "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 - }, - "loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "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": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - } - }, - "react-dropzone": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz", - "integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==", - "requires": { - "attr-accept": "^1.1.3", - "file-selector": "^0.1.8", - "prop-types": "^15.6.2", - "prop-types-extra": "^1.1.0" - } - }, - "react-error-boundary": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", - "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", - "requires": { - "@babel/runtime": "^7.12.5" - } - }, - "react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", - "dev": true - }, - "react-fast-compare": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" - }, - "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==" - }, - "react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", - "dev": true - }, - "react-router": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.3.tgz", - "integrity": "sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - } - }, - "react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.3", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - } - }, - "react-scripts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", - "dev": true, - "requires": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "fsevents": "^2.3.2", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "dependencies": { - "@types/eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "eslint-webpack-plugin": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz", - "integrity": "sha512-xSucskTN9tOkfW7so4EaiFIkulWLXwCB/15H917lR6pTv0Zot6/fetFucmENRb7J5whVSFKIvwnrnsa78SG2yg==", - "dev": true, - "requires": { - "@types/eslint": "^7.28.2", - "jest-worker": "^27.3.1", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "schema-utils": "^3.1.1" - } - }, - "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "react-shallow-renderer": { - "version": "16.14.1", - "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz", - "integrity": "sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0" - } - }, - "react-test-renderer": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", - "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "react-is": "^17.0.2", - "react-shallow-renderer": "^16.13.1", - "scheduler": "^0.20.2" - }, - "dependencies": { - "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 - } - } - }, - "react-virtualized": { - "version": "9.22.3", - "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz", - "integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==", - "requires": { - "@babel/runtime": "^7.7.2", - "clsx": "^1.0.4", - "dom-helpers": "^5.1.3", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-lifecycles-compat": "^3.0.4" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dev": true, - "requires": { - "minimatch": "^3.0.5" - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "reflect.ownkeys": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", - "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=", - "dev": true - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", - "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "regexpu-core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", - "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", - "dev": true, - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^9.0.0", - "regjsgen": "^0.5.2", - "regjsparser": "^0.7.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "regjsparser": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", - "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "requireindex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", - "integrity": "sha1-5UBLgVV+91225JxacgBIk/4D4WI=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", - "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", - "dev": true, - "requires": { - "is-core-module": "^2.8.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "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-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "dev": true, - "requires": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - } - } - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "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" - } - }, - "robust-predicates": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", - "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" - }, - "rollup": { - "version": "2.64.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.64.0.tgz", - "integrity": "sha512-+c+lbw1lexBKSMb1yxGDVfJ+vchJH3qLbmavR+awDinTDA2C5Ug9u7lkOzj62SCu0PKUExsW36tpgW7Fmpn3yQ==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "rrule": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.7.1.tgz", - "integrity": "sha512-4p20u/1U7WqR3Nb1hOUrm0u1nSI7sO93ZUVZEZ5HeF6Gr5OlJuyhwEGRvUHq8ZfrPsq5gfa5b9dqnUs/kPqpIw==", - "requires": { - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - } - } - }, - "rst-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", - "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", - "dev": true, - "requires": { - "lodash.flattendeep": "^4.4.0", - "nearley": "^2.7.10" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==", - "dev": true - }, - "sass-loader": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", - "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", - "dev": true, - "requires": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selfsigned": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", - "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", - "dev": true, - "requires": { - "node-forge": "^1.2.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shell-quote": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", - "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true - }, - "source-map-loader": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", - "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "stackframe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", - "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "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 - } - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trim": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz", - "integrity": "sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - }, - "dependencies": { - "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 - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "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 - }, - "style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "dev": true, - "requires": {} - }, - "style-mod": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz", - "integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==" - }, - "styled-components": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz", - "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", - "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" - } - }, - "stylehacks": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", - "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", - "dev": true, - "requires": { - "browserslist": "^4.16.0", - "postcss-selector-parser": "^6.0.4" - } - }, - "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" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "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==", - "dev": true - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "dev": true - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "dev": true - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - } - } - }, - "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 - }, - "tabbable": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", - "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" - }, - "tailwindcss": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.15.tgz", - "integrity": "sha512-bT2iy7FtjwgsXik4ZoJnHXR+SRCiGR1W95fVqpLZebr64m4ahwUwRbIAc5w5+2fzr1YF4Ct2eI7dojMRRl8sVQ==", - "dev": true, - "requires": { - "arg": "^5.0.1", - "chalk": "^4.1.2", - "chokidar": "^3.5.2", - "color-name": "^1.1.4", - "cosmiconfig": "^7.0.1", - "detective": "^5.2.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.7", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "normalize-path": "^3.0.0", - "object-hash": "^2.2.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.0", - "postcss-nested": "5.0.6", - "postcss-selector-parser": "^6.0.8", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.21.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "arg": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", - "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", - "dev": true - }, - "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" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "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" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "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" - } - }, - "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 - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", - "dev": true - }, - "temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true - }, - "tempy": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "dependencies": { - "type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "dev": true - } - } - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.0.tgz", - "integrity": "sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", - "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", - "dev": true, - "requires": { - "jest-worker": "^27.4.1", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tiny-invariant": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", - "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" - }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "tippy.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-5.1.2.tgz", - "integrity": "sha512-Qtrv2wqbRbaKMUb6bWWBQWPayvcDKNrGlvihxtsyowhT7RLGEh1STWuy6EMXC6QLkfKPB2MLnf8W2mzql9VDAw==", - "requires": { - "popper.js": "^1.16.0" - } - }, - "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" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "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==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true - }, - "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - }, - "dependencies": { - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "dev": true - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": 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": { - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - } - } - }, - "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "dev": true, - "peer": true - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", - "dev": true - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "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==", - "dev": true - }, - "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, - "value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-keyname": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz", - "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==" - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "webpack": { - "version": "5.66.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.66.0.tgz", - "integrity": "sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", - "webpack-sources": "^3.2.2" - }, - "dependencies": { - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "enhanced-resolve": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", - "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - } - } - }, - "webpack-dev-middleware": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", - "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", - "dev": true, - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.2.2", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "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 - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "webpack-dev-server": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", - "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", - "dev": true, - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/serve-index": "^1.9.1", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.2.2", - "ansi-html-community": "^0.0.8", - "bonjour": "^3.5.0", - "chokidar": "^3.5.2", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "default-gateway": "^6.0.3", - "del": "^6.0.0", - "express": "^4.17.1", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.0", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "portfinder": "^1.0.28", - "schema-utils": "^4.0.0", - "selfsigned": "^2.0.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "spdy": "^4.0.2", - "strip-ansi": "^7.0.0", - "webpack-dev-middleware": "^5.3.0", - "ws": "^8.1.0" - }, - "dependencies": { - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "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" - } - }, - "http-proxy-middleware": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", - "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", - "dev": true, - "requires": { - "@types/http-proxy": "^1.17.5", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "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 - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "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, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "ws": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", - "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", - "dev": true, - "requires": {} - } - } - }, - "webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "dev": true, - "requires": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "dependencies": { - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "dev": true, - "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - } - } - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", - "dev": true - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "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": { - "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" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "workbox-background-sync": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.4.2.tgz", - "integrity": "sha512-P7c8uG5X2k+DMICH9xeSA9eUlCOjHHYoB42Rq+RtUpuwBxUOflAXR1zdsMWj81LopE4gjKXlTw7BFd1BDAHo7g==", - "dev": true, - "requires": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" - } - }, - "workbox-broadcast-update": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.4.2.tgz", - "integrity": "sha512-qnBwQyE0+PWFFc/n4ISXINE49m44gbEreJUYt2ldGH3+CNrLmJ1egJOOyUqqu9R4Eb7QrXcmB34ClXG7S37LbA==", - "dev": true, - "requires": { - "workbox-core": "6.4.2" - } - }, - "workbox-build": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.4.2.tgz", - "integrity": "sha512-WMdYLhDIsuzViOTXDH+tJ1GijkFp5khSYolnxR/11zmfhNDtuo7jof72xPGFy+KRpsz6tug39RhivCj77qqO0w==", - "dev": true, - "requires": { - "@apideck/better-ajv-errors": "^0.3.1", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "@babel/runtime": "^7.11.2", - "@rollup/plugin-babel": "^5.2.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-replace": "^2.4.1", - "@surma/rollup-plugin-off-main-thread": "^2.2.3", - "ajv": "^8.6.0", - "common-tags": "^1.8.0", - "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "lodash": "^4.17.20", - "pretty-bytes": "^5.3.0", - "rollup": "^2.43.1", - "rollup-plugin-terser": "^7.0.0", - "source-map": "^0.8.0-beta.0", - "source-map-url": "^0.4.0", - "stringify-object": "^3.3.0", - "strip-comments": "^2.0.1", - "tempy": "^0.6.0", - "upath": "^1.2.0", - "workbox-background-sync": "6.4.2", - "workbox-broadcast-update": "6.4.2", - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-google-analytics": "6.4.2", - "workbox-navigation-preload": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-range-requests": "6.4.2", - "workbox-recipes": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2", - "workbox-streams": "6.4.2", - "workbox-sw": "6.4.2", - "workbox-window": "6.4.2" - }, - "dependencies": { - "@apideck/better-ajv-errors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.2.tgz", - "integrity": "sha512-JdEazx7qiVqTBzzBl5rolRwl5cmhihjfIcpqRzIZjtT6b18liVmDn/VlWpqW4C/qP2hrFFMLRV1wlex8ZVBPTg==", - "dev": true, - "requires": { - "json-schema": "^0.4.0", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - } - }, - "ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "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" - } - }, - "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 - }, - "source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "requires": { - "whatwg-url": "^7.0.0" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "workbox-cacheable-response": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.4.2.tgz", - "integrity": "sha512-9FE1W/cKffk1AJzImxgEN0ceWpyz1tqNjZVtA3/LAvYL3AC5SbIkhc7ZCO82WmO9IjTfu8Vut2X/C7ViMSF7TA==", - "dev": true, - "requires": { - "workbox-core": "6.4.2" - } - }, - "workbox-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.4.2.tgz", - "integrity": "sha512-1U6cdEYPcajRXiboSlpJx6U7TvhIKbxRRerfepAJu2hniKwJ3DHILjpU/zx3yvzSBCWcNJDoFalf7Vgd7ey/rw==", - "dev": true - }, - "workbox-expiration": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.4.2.tgz", - "integrity": "sha512-0hbpBj0tDnW+DZOUmwZqntB/8xrXOgO34i7s00Si/VlFJvvpRKg1leXdHHU8ykoSBd6+F2KDcMP3swoCi5guLw==", - "dev": true, - "requires": { - "idb": "^6.1.4", - "workbox-core": "6.4.2" - } - }, - "workbox-google-analytics": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.4.2.tgz", - "integrity": "sha512-u+gxs3jXovPb1oul4CTBOb+T9fS1oZG+ZE6AzS7l40vnyfJV79DaLBvlpEZfXGv3CjMdV1sT/ltdOrKzo7HcGw==", - "dev": true, - "requires": { - "workbox-background-sync": "6.4.2", - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" - } - }, - "workbox-navigation-preload": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.4.2.tgz", - "integrity": "sha512-viyejlCtlKsbJCBHwhSBbWc57MwPXvUrc8P7d+87AxBGPU+JuWkT6nvBANgVgFz6FUhCvRC8aYt+B1helo166g==", - "dev": true, - "requires": { - "workbox-core": "6.4.2" - } - }, - "workbox-precaching": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.4.2.tgz", - "integrity": "sha512-CZ6uwFN/2wb4noHVlALL7UqPFbLfez/9S2GAzGAb0Sk876ul9ukRKPJJ6gtsxfE2HSTwqwuyNVa6xWyeyJ1XSA==", - "dev": true, - "requires": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" - } - }, - "workbox-range-requests": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.4.2.tgz", - "integrity": "sha512-SowF3z69hr3Po/w7+xarWfzxJX/3Fo0uSG72Zg4g5FWWnHpq2zPvgbWerBZIa81zpJVUdYpMa3akJJsv+LaO1Q==", - "dev": true, - "requires": { - "workbox-core": "6.4.2" - } - }, - "workbox-recipes": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.4.2.tgz", - "integrity": "sha512-/oVxlZFpAjFVbY+3PoGEXe8qyvtmqMrTdWhbOfbwokNFtUZ/JCtanDKgwDv9x3AebqGAoJRvQNSru0F4nG+gWA==", - "dev": true, - "requires": { - "workbox-cacheable-response": "6.4.2", - "workbox-core": "6.4.2", - "workbox-expiration": "6.4.2", - "workbox-precaching": "6.4.2", - "workbox-routing": "6.4.2", - "workbox-strategies": "6.4.2" - } - }, - "workbox-routing": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.4.2.tgz", - "integrity": "sha512-0ss/n9PAcHjTy4Ad7l2puuod4WtsnRYu9BrmHcu6Dk4PgWeJo1t5VnGufPxNtcuyPGQ3OdnMdlmhMJ57sSrrSw==", - "dev": true, - "requires": { - "workbox-core": "6.4.2" - } - }, - "workbox-strategies": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.4.2.tgz", - "integrity": "sha512-YXh9E9dZGEO1EiPC3jPe2CbztO5WT8Ruj8wiYZM56XqEJp5YlGTtqRjghV+JovWOqkWdR+amJpV31KPWQUvn1Q==", - "dev": true, - "requires": { - "workbox-core": "6.4.2" - } - }, - "workbox-streams": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.4.2.tgz", - "integrity": "sha512-ROEGlZHGVEgpa5bOZefiJEVsi5PsFjJG9Xd+wnDbApsCO9xq9rYFopF+IRq9tChyYzhBnyk2hJxbQVWphz3sog==", - "dev": true, - "requires": { - "workbox-core": "6.4.2", - "workbox-routing": "6.4.2" - } - }, - "workbox-sw": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.4.2.tgz", - "integrity": "sha512-A2qdu9TLktfIM5NE/8+yYwfWu+JgDaCkbo5ikrky2c7r9v2X6DcJ+zSLphNHHLwM/0eVk5XVf1mC5HGhYpMhhg==", - "dev": true - }, - "workbox-webpack-plugin": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.4.2.tgz", - "integrity": "sha512-CiEwM6kaJRkx1cP5xHksn13abTzUqMHiMMlp5Eh/v4wRcedgDTyv6Uo8+Hg9MurRbHDosO5suaPyF9uwVr4/CQ==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "^2.1.0", - "pretty-bytes": "^5.4.1", - "source-map-url": "^0.4.0", - "upath": "^1.2.0", - "webpack-sources": "^1.4.3", - "workbox-build": "6.4.2" - }, - "dependencies": { - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "workbox-window": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.4.2.tgz", - "integrity": "sha512-KVyRKmrJg7iB+uym/B/CnEUEFG9CvnTU1Bq5xpXHbtgD9l+ShDekSl1wYpqw/O0JfeeQVOFb8CiNfvnwWwqnWQ==", - "dev": true, - "requires": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.4.2" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", - "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", - "dev": true, - "requires": {} - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "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 - } - } -} diff --git a/awx/ui/package.json b/awx/ui/package.json deleted file mode 100644 index c729e16e7db8..000000000000 --- a/awx/ui/package.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "name": "ui", - "homepage": ".", - "private": true, - "engines": { - "node": ">=16.13.1" - }, - "dependencies": { - "@lingui/react": "3.14.0", - "@patternfly/patternfly": "4.217.1", - "@patternfly/react-core": "^4.264.0", - "@patternfly/react-icons": "4.92.10", - "@patternfly/react-table": "4.108.0", - "ace-builds": "^1.10.1", - "ansi-to-html": "0.7.2", - "axios": "0.27.2", - "codemirror": "^6.0.1", - "d3": "7.6.1", - "dagre": "^0.8.4", - "dompurify": "2.4.0", - "formik": "2.2.9", - "has-ansi": "5.0.1", - "html-entities": "2.3.2", - "js-yaml": "4.1.0", - "luxon": "^3.1.1", - "prop-types": "^15.8.1", - "react": "17.0.2", - "react-ace": "^10.1.0", - "react-dom": "17.0.2", - "react-error-boundary": "^3.1.4", - "react-router-dom": "^5.3.3", - "react-virtualized": "^9.21.1", - "rrule": "2.7.1", - "styled-components": "5.3.6" - }, - "devDependencies": { - "@babel/core": "^7.16.10", - "@babel/eslint-parser": "^7.16.5", - "@babel/eslint-plugin": "^7.16.5", - "@babel/plugin-syntax-jsx": "7.16.7", - "@babel/polyfill": "^7.8.7", - "@babel/preset-react": "7.16.7", - "@cypress/instrument-cra": "^1.4.0", - "@lingui/cli": "^3.7.1", - "@lingui/loader": "3.15.0", - "@lingui/macro": "^3.7.1", - "@nteract/mockument": "^1.0.4", - "@testing-library/jest-dom": "^5.16.2", - "@testing-library/react": "^12.1.5", - "@testing-library/user-event": "14.4.3", - "@wojtekmaj/enzyme-adapter-react-17": "0.6.5", - "babel-plugin-macros": "3.1.0", - "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.14.0", - "enzyme-to-json": "^3.3.5", - "eslint": "^8.7.0", - "eslint-config-airbnb": "19.0.4", - "eslint-config-prettier": "8.3.0", - "eslint-import-resolver-webpack": "0.13.2", - "eslint-plugin-i18next": "5.2.1", - "eslint-plugin-import": "2.25.4", - "eslint-plugin-jsx-a11y": "6.5.1", - "eslint-plugin-react": "7.28.0", - "eslint-plugin-react-hooks": "4.3.0", - "http-proxy-middleware": "^1.0.3", - "jest-websocket-mock": "^2.0.2", - "mock-socket": "^9.1.3", - "prettier": "2.3.2", - "react-scripts": "5.0.1" - }, - "scripts": { - "prelint": "lingui compile", - "prestart": "lingui compile", - "prestart-instrumented": "lingui compile", - "pretest": "lingui compile", - "pretest-watch": "lingui compile", - "start": "GENERATE_SOURCEMAP=false ESLINT_NO_DEV_ERRORS=true PORT=3001 HTTPS=true DANGEROUSLY_DISABLE_HOST_CHECK=true react-scripts start", - "start-instrumented": "ESLINT_NO_DEV_ERRORS=true DEBUG=instrument-cra PORT=3001 HTTPS=true DANGEROUSLY_DISABLE_HOST_CHECK=true react-scripts -r @cypress/instrument-cra start", - "build": "INLINE_RUNTIME_CHUNK=false react-scripts build", - "test": "TZ='UTC' react-scripts test --watchAll=false", - "test-screens": "TZ='UTC' react-scripts test screens --watchAll=false", - "test-general": "TZ='UTC' react-scripts test --testPathIgnorePatterns='/src/screens/' --watchAll=false", - "test-watch": "TZ='UTC' react-scripts test", - "eject": "react-scripts eject", - "lint": "eslint --ext .js --ext .jsx .", - "extract-strings": "lingui extract", - "extract-template": "lingui extract-template", - "compile-strings": "lingui compile", - "prettier": "prettier --write \"src/**/*.{js,jsx,scss}\"", - "prettier-check": "prettier --check \"src/**/*.{js,jsx,scss}\"" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "jest": { - "snapshotSerializers": [ - "enzyme-to-json/serializer" - ], - "collectCoverageFrom": [ - "src/**/*.{js,jsx}", - "testUtils/**/*.{js,jsx}" - ], - "coveragePathIgnorePatterns": [ - "/src/locales", - "index.js" - ], - "transformIgnorePatterns": [ - "/node_modules/(?!d3)/", - "/node_modules/(?!has-ansi)/" - ] - } -} diff --git a/awx/ui/placeholder_index_awx.html b/awx/ui/placeholder_index_awx.html new file mode 100644 index 000000000000..3dabf3711fb7 --- /dev/null +++ b/awx/ui/placeholder_index_awx.html @@ -0,0 +1,9 @@ + + + UI Missing + +
+

Oops... Looks like the UI wasn't properly built

+
+ + diff --git a/awx/ui/public/index.html b/awx/ui/public/index.html deleted file mode 100644 index 2478bd616b47..000000000000 --- a/awx/ui/public/index.html +++ /dev/null @@ -1,49 +0,0 @@ - -<% if (process.env.NODE_ENV === 'production') { %> - {% load static %} -<% } %> - - - - <% if (process.env.NODE_ENV === 'production') { %> - - - - <% } else { %> - - <% } %> - - - - - - - - <% if (process.env.NODE_ENV === 'production') { %> -
- <% } else { %> -
- <% } %> - - diff --git a/awx/ui/public/installing.html b/awx/ui/public/installing.html index c13091e9d6b6..accfbcce6811 100644 --- a/awx/ui/public/installing.html +++ b/awx/ui/public/installing.html @@ -5,7 +5,11 @@ {{ title }} diff --git a/awx/ui/public/static/css/assets/fonts/RedHatDisplay/RedHatDisplay-Medium.woff b/awx/ui/public/static/css/assets/fonts/RedHatDisplay/RedHatDisplay-Medium.woff deleted file mode 100644 index 7a88f9dfec7d..000000000000 Binary files a/awx/ui/public/static/css/assets/fonts/RedHatDisplay/RedHatDisplay-Medium.woff and /dev/null differ diff --git a/awx/ui/public/static/css/assets/fonts/RedHatText/RedHatText-Regular.woff b/awx/ui/public/static/css/assets/fonts/RedHatText/RedHatText-Regular.woff deleted file mode 100644 index b0e94af5eeac..000000000000 Binary files a/awx/ui/public/static/css/assets/fonts/RedHatText/RedHatText-Regular.woff and /dev/null differ diff --git a/awx/ui/public/static/css/patternfly.min.css b/awx/ui/public/static/css/patternfly.min.css deleted file mode 100644 index 67bb72558c29..000000000000 --- a/awx/ui/public/static/css/patternfly.min.css +++ /dev/null @@ -1,2 +0,0 @@ -@charset "UTF-8";.pf-c-accordion,.pf-c-alert,.pf-c-banner.pf-m-info,.pf-c-banner.pf-m-warning,.pf-c-calendar-month,.pf-c-chip,.pf-c-chip-group,.pf-c-context-selector__menu,.pf-c-data-list,.pf-c-form-control,.pf-c-input-group,.pf-c-menu,.pf-c-page__sidebar.pf-m-light,.pf-c-select,.pf-c-table,.pf-t-light{--pf-global--Color--100:var(--pf-global--Color--dark-100);--pf-global--Color--200:var(--pf-global--Color--dark-200);--pf-global--BorderColor--100:var(--pf-global--BorderColor--dark-100);--pf-global--primary-color--100:var(--pf-global--primary-color--dark-100);--pf-global--link--Color:var(--pf-global--link--Color--dark);--pf-global--link--Color--hover:var(--pf-global--link--Color--dark--hover);--pf-global--BackgroundColor--100:var(--pf-global--BackgroundColor--light-100)}.pf-c-about-modal-box,.pf-c-banner,.pf-c-login__footer,.pf-c-login__header,.pf-c-page__header,.pf-c-page__main-section[class*=pf-m-dark-],.pf-c-wizard__header,.pf-t-dark{--pf-global--Color--100:var(--pf-global--Color--light-100);--pf-global--Color--200:var(--pf-global--Color--light-200);--pf-global--BorderColor--100:var(--pf-global--BorderColor--light-100);--pf-global--primary-color--100:var(--pf-global--primary-color--light-100);--pf-global--link--Color:var(--pf-global--link--Color--light);--pf-global--link--Color--hover:var(--pf-global--link--Color--light);--pf-global--BackgroundColor--100:var(--pf-global--BackgroundColor--dark-100)}.pf-c-about-modal-box .pf-c-card,.pf-c-banner .pf-c-card,.pf-c-login__footer .pf-c-card,.pf-c-login__header .pf-c-card,.pf-c-page__header .pf-c-card,.pf-c-page__main-section[class*=pf-m-dark-] .pf-c-card,.pf-c-wizard__header .pf-c-card,.pf-t-dark .pf-c-card{--pf-c-card--BackgroundColor:var(--pf-global--BackgroundColor--dark-transparent-200)}.pf-c-about-modal-box .pf-c-button,.pf-c-banner .pf-c-button,.pf-c-login__footer .pf-c-button,.pf-c-login__header .pf-c-button,.pf-c-page__header .pf-c-button,.pf-c-page__main-section[class*=pf-m-dark-] .pf-c-button,.pf-c-wizard__header .pf-c-button,.pf-t-dark .pf-c-button{--pf-c-button--m-primary--Color:var(--pf-global--primary-color--dark-100);--pf-c-button--m-primary--hover--Color:var(--pf-global--primary-color--dark-100);--pf-c-button--m-primary--focus--Color:var(--pf-global--primary-color--dark-100);--pf-c-button--m-primary--active--Color:var(--pf-global--primary-color--dark-100);--pf-c-button--m-primary--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-button--m-primary--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-button--m-primary--focus--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-button--m-primary--active--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-button--m-secondary--Color:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--hover--Color:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--focus--Color:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--active--Color:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--BorderColor:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--hover--BorderColor:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--focus--BorderColor:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--active--BorderColor:var(--pf-global--Color--light-100)}.pf-c-data-list__item-action,.pf-c-page__header-tools-group,.pf-c-page__header-tools-item,.pf-c-table tr>*{--pf-hidden-visible--visible--Visibility:visible;--pf-hidden-visible--hidden--Display:none;--pf-hidden-visible--hidden--Visibility:hidden;--pf-hidden-visible--Display:var(--pf-hidden-visible--visible--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--visible--Visibility);display:var(--pf-hidden-visible--Display);visibility:var(--pf-hidden-visible--Visibility)}.pf-c-table tr>.pf-m-hidden,.pf-m-hidden.pf-c-data-list__item-action,.pf-m-hidden.pf-c-page__header-tools-group,.pf-m-hidden.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--hidden--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--hidden--Visibility)}@media screen and (min-width:576px){.pf-c-table tr>.pf-m-hidden-on-sm,.pf-m-hidden-on-sm.pf-c-data-list__item-action,.pf-m-hidden-on-sm.pf-c-page__header-tools-group,.pf-m-hidden-on-sm.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--hidden--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--hidden--Visibility)}.pf-c-table tr>.pf-m-visible-on-sm,.pf-m-visible-on-sm.pf-c-data-list__item-action,.pf-m-visible-on-sm.pf-c-page__header-tools-group,.pf-m-visible-on-sm.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--visible--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--visible--Visibility)}}@media screen and (min-width:768px){.pf-c-table tr>.pf-m-hidden-on-md,.pf-m-hidden-on-md.pf-c-data-list__item-action,.pf-m-hidden-on-md.pf-c-page__header-tools-group,.pf-m-hidden-on-md.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--hidden--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--hidden--Visibility)}.pf-c-table tr>.pf-m-visible-on-md,.pf-m-visible-on-md.pf-c-data-list__item-action,.pf-m-visible-on-md.pf-c-page__header-tools-group,.pf-m-visible-on-md.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--visible--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--visible--Visibility)}}@media screen and (min-width:992px){.pf-c-table tr>.pf-m-hidden-on-lg,.pf-m-hidden-on-lg.pf-c-data-list__item-action,.pf-m-hidden-on-lg.pf-c-page__header-tools-group,.pf-m-hidden-on-lg.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--hidden--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--hidden--Visibility)}.pf-c-table tr>.pf-m-visible-on-lg,.pf-m-visible-on-lg.pf-c-data-list__item-action,.pf-m-visible-on-lg.pf-c-page__header-tools-group,.pf-m-visible-on-lg.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--visible--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--visible--Visibility)}}@media screen and (min-width:1200px){.pf-c-table tr>.pf-m-hidden-on-xl,.pf-m-hidden-on-xl.pf-c-data-list__item-action,.pf-m-hidden-on-xl.pf-c-page__header-tools-group,.pf-m-hidden-on-xl.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--hidden--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--hidden--Visibility)}.pf-c-table tr>.pf-m-visible-on-xl,.pf-m-visible-on-xl.pf-c-data-list__item-action,.pf-m-visible-on-xl.pf-c-page__header-tools-group,.pf-m-visible-on-xl.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--visible--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--visible--Visibility)}}@media screen and (min-width:1450px){.pf-c-table tr>.pf-m-hidden-on-2xl,.pf-m-hidden-on-2xl.pf-c-data-list__item-action,.pf-m-hidden-on-2xl.pf-c-page__header-tools-group,.pf-m-hidden-on-2xl.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--hidden--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--hidden--Visibility)}.pf-c-table tr>.pf-m-visible-on-2xl,.pf-m-visible-on-2xl.pf-c-data-list__item-action,.pf-m-visible-on-2xl.pf-c-page__header-tools-group,.pf-m-visible-on-2xl.pf-c-page__header-tools-item{--pf-hidden-visible--Display:var(--pf-hidden-visible--visible--Display);--pf-hidden-visible--Visibility:var(--pf-hidden-visible--visible--Visibility)}}:root{--pf-global--palette--black-100:#fafafa;--pf-global--palette--black-150:#f5f5f5;--pf-global--palette--black-200:#f0f0f0;--pf-global--palette--black-300:#d2d2d2;--pf-global--palette--black-400:#b8bbbe;--pf-global--palette--black-500:#8a8d90;--pf-global--palette--black-600:#6a6e73;--pf-global--palette--black-700:#4f5255;--pf-global--palette--black-800:#3c3f42;--pf-global--palette--black-850:#212427;--pf-global--palette--black-900:#151515;--pf-global--palette--black-1000:#030303;--pf-global--palette--blue-50:#e7f1fa;--pf-global--palette--blue-100:#bee1f4;--pf-global--palette--blue-200:#73bcf7;--pf-global--palette--blue-300:#2b9af3;--pf-global--palette--blue-400:#06c;--pf-global--palette--blue-500:#004080;--pf-global--palette--blue-600:#002952;--pf-global--palette--blue-700:#001223;--pf-global--palette--cyan-50:#f2f9f9;--pf-global--palette--cyan-100:#a2d9d9;--pf-global--palette--cyan-200:#73c5c5;--pf-global--palette--cyan-300:#009596;--pf-global--palette--cyan-400:#005f60;--pf-global--palette--cyan-500:#003737;--pf-global--palette--cyan-600:#002323;--pf-global--palette--cyan-700:#000f0f;--pf-global--palette--gold-50:#fdf7e7;--pf-global--palette--gold-100:#f9e0a2;--pf-global--palette--gold-200:#f6d173;--pf-global--palette--gold-300:#f4c145;--pf-global--palette--gold-400:#f0ab00;--pf-global--palette--gold-500:#c58c00;--pf-global--palette--gold-600:#795600;--pf-global--palette--gold-700:#3d2c00;--pf-global--palette--green-50:#f3faf2;--pf-global--palette--green-100:#bde5b8;--pf-global--palette--green-200:#95d58e;--pf-global--palette--green-300:#6ec664;--pf-global--palette--green-400:#5ba352;--pf-global--palette--green-500:#3e8635;--pf-global--palette--green-600:#1e4f18;--pf-global--palette--green-700:#0f280d;--pf-global--palette--light-blue-100:#beedf9;--pf-global--palette--light-blue-200:#7cdbf3;--pf-global--palette--light-blue-300:#35caed;--pf-global--palette--light-blue-400:#00b9e4;--pf-global--palette--light-blue-500:#008bad;--pf-global--palette--light-blue-600:#005c73;--pf-global--palette--light-blue-700:#002d39;--pf-global--palette--light-green-100:#e4f5bc;--pf-global--palette--light-green-200:#c8eb79;--pf-global--palette--light-green-300:#ace12e;--pf-global--palette--light-green-400:#92d400;--pf-global--palette--light-green-500:#6ca100;--pf-global--palette--light-green-600:#486b00;--pf-global--palette--light-green-700:#253600;--pf-global--palette--orange-100:#f4b678;--pf-global--palette--orange-200:#ef9234;--pf-global--palette--orange-300:#ec7a08;--pf-global--palette--orange-400:#c46100;--pf-global--palette--orange-500:#8f4700;--pf-global--palette--orange-600:#773d00;--pf-global--palette--orange-700:#3b1f00;--pf-global--palette--purple-50:#f2f0fc;--pf-global--palette--purple-100:#cbc1ff;--pf-global--palette--purple-200:#b2a3ff;--pf-global--palette--purple-300:#a18fff;--pf-global--palette--purple-400:#8476d1;--pf-global--palette--purple-500:#6753ac;--pf-global--palette--purple-600:#40199a;--pf-global--palette--purple-700:#1f0066;--pf-global--palette--red-50:#faeae8;--pf-global--palette--red-100:#c9190b;--pf-global--palette--red-200:#a30000;--pf-global--palette--red-300:#7d1007;--pf-global--palette--red-400:#470000;--pf-global--palette--red-500:#2c0000;--pf-global--palette--white:#fff;--pf-global--BackgroundColor--100:#fff;--pf-global--BackgroundColor--200:#f0f0f0;--pf-global--BackgroundColor--light-100:#fff;--pf-global--BackgroundColor--light-200:#fafafa;--pf-global--BackgroundColor--light-300:#f0f0f0;--pf-global--BackgroundColor--dark-100:#151515;--pf-global--BackgroundColor--dark-200:#3c3f42;--pf-global--BackgroundColor--dark-300:#212427;--pf-global--BackgroundColor--dark-400:#4f5255;--pf-global--BackgroundColor--dark-transparent-100:rgba(3,3,3,0.62);--pf-global--BackgroundColor--dark-transparent-200:rgba(3,3,3,0.32);--pf-global--Color--100:#151515;--pf-global--Color--200:#6a6e73;--pf-global--Color--300:#3c3f42;--pf-global--Color--400:#8a8d90;--pf-global--Color--light-100:#fff;--pf-global--Color--light-200:#f0f0f0;--pf-global--Color--light-300:#d2d2d2;--pf-global--Color--dark-100:#151515;--pf-global--Color--dark-200:#6a6e73;--pf-global--active-color--100:#06c;--pf-global--active-color--200:#bee1f4;--pf-global--active-color--300:#2b9af3;--pf-global--active-color--400:#73bcf7;--pf-global--disabled-color--100:#6a6e73;--pf-global--disabled-color--200:#d2d2d2;--pf-global--disabled-color--300:#f0f0f0;--pf-global--primary-color--100:#06c;--pf-global--primary-color--200:#004080;--pf-global--primary-color--light-100:#73bcf7;--pf-global--primary-color--dark-100:#06c;--pf-global--secondary-color--100:#6a6e73;--pf-global--default-color--100:#73c5c5;--pf-global--default-color--200:#009596;--pf-global--default-color--300:#003737;--pf-global--success-color--100:#3e8635;--pf-global--success-color--200:#1e4f18;--pf-global--info-color--100:#2b9af3;--pf-global--info-color--200:#002952;--pf-global--warning-color--100:#f0ab00;--pf-global--warning-color--200:#795600;--pf-global--danger-color--100:#c9190b;--pf-global--danger-color--200:#a30000;--pf-global--danger-color--300:#470000;--pf-global--BoxShadow--sm:0 0.0625rem 0.125rem 0 rgba(3,3,3,0.12),0 0 0.125rem 0 rgba(3,3,3,0.06);--pf-global--BoxShadow--sm-top:0 -0.125rem 0.25rem -0.0625rem rgba(3,3,3,0.16);--pf-global--BoxShadow--sm-right:0.125rem 0 0.25rem -0.0625rem rgba(3,3,3,0.16);--pf-global--BoxShadow--sm-bottom:0 0.125rem 0.25rem -0.0625rem rgba(3,3,3,0.16);--pf-global--BoxShadow--sm-left:-0.125rem 0 0.25rem -0.0625rem rgba(3,3,3,0.16);--pf-global--BoxShadow--md:0 0.25rem 0.5rem 0rem rgba(3,3,3,0.12),0 0 0.25rem 0 rgba(3,3,3,0.06);--pf-global--BoxShadow--md-top:0 -0.5rem 0.5rem -0.375rem rgba(3,3,3,0.18);--pf-global--BoxShadow--md-right:0.5rem 0 0.5rem -0.375rem rgba(3,3,3,0.18);--pf-global--BoxShadow--md-bottom:0 0.5rem 0.5rem -0.375rem rgba(3,3,3,0.18);--pf-global--BoxShadow--md-left:-0.5rem 0 0.5rem -0.375rem rgba(3,3,3,0.18);--pf-global--BoxShadow--lg:0 0.5rem 1rem 0 rgba(3,3,3,0.16),0 0 0.375rem 0 rgba(3,3,3,0.08);--pf-global--BoxShadow--lg-top:0 -0.75rem 0.75rem -0.5rem rgba(3,3,3,0.18);--pf-global--BoxShadow--lg-right:0.75rem 0 0.75rem -0.5rem rgba(3,3,3,0.18);--pf-global--BoxShadow--lg-bottom:0 0.75rem 0.75rem -0.5rem rgba(3,3,3,0.18);--pf-global--BoxShadow--lg-left:-0.75rem 0 0.75rem -0.5rem rgba(3,3,3,0.18);--pf-global--BoxShadow--xl:0 1rem 2rem 0 rgba(3,3,3,0.16),0 0 0.5rem 0 rgba(3,3,3,0.1);--pf-global--BoxShadow--xl-top:0 -1rem 1rem -0.5rem rgba(3,3,3,0.2);--pf-global--BoxShadow--xl-right:1rem 0 1rem -0.5rem rgba(3,3,3,0.2);--pf-global--BoxShadow--xl-bottom:0 1rem 1rem -0.5rem rgba(3,3,3,0.2);--pf-global--BoxShadow--xl-left:-1rem 0 1rem -0.5rem rgba(3,3,3,0.2);--pf-global--BoxShadow--inset:inset 0 0 0.625rem 0 rgba(3,3,3,0.25);--pf-global--font-path:"./assets/fonts";--pf-global--fonticon-path:"./assets/pficon";--pf-global--spacer--xs:0.25rem;--pf-global--spacer--sm:0.5rem;--pf-global--spacer--md:1rem;--pf-global--spacer--lg:1.5rem;--pf-global--spacer--xl:2rem;--pf-global--spacer--2xl:3rem;--pf-global--spacer--3xl:4rem;--pf-global--spacer--4xl:5rem;--pf-global--spacer--form-element:0.375rem;--pf-global--gutter:1rem;--pf-global--gutter--md:1.5rem;--pf-global--ZIndex--xs:100;--pf-global--ZIndex--sm:200;--pf-global--ZIndex--md:300;--pf-global--ZIndex--lg:400;--pf-global--ZIndex--xl:500;--pf-global--ZIndex--2xl:600;--pf-global--breakpoint--xs:0;--pf-global--breakpoint--sm:576px;--pf-global--breakpoint--md:768px;--pf-global--breakpoint--lg:992px;--pf-global--breakpoint--xl:1200px;--pf-global--breakpoint--2xl:1450px;--pf-global--link--Color:#06c;--pf-global--link--Color--hover:#004080;--pf-global--link--Color--light:#2b9af3;--pf-global--link--Color--light--hover:#73bcf7;--pf-global--link--Color--dark:#06c;--pf-global--link--Color--dark--hover:#004080;--pf-global--link--TextDecoration:none;--pf-global--link--TextDecoration--hover:underline;--pf-global--BorderWidth--sm:1px;--pf-global--BorderWidth--md:2px;--pf-global--BorderWidth--lg:3px;--pf-global--BorderWidth--xl:4px;--pf-global--BorderColor--100:#d2d2d2;--pf-global--BorderColor--200:#8a8d90;--pf-global--BorderColor--300:#f0f0f0;--pf-global--BorderColor--dark-100:#d2d2d2;--pf-global--BorderColor--light-100:#b8bbbe;--pf-global--BorderRadius--sm:3px;--pf-global--BorderRadius--lg:30em;--pf-global--icon--Color--light:#6a6e73;--pf-global--icon--Color--dark:#151515;--pf-global--icon--FontSize--sm:0.625rem;--pf-global--icon--FontSize--md:1.125rem;--pf-global--icon--FontSize--lg:1.5rem;--pf-global--icon--FontSize--xl:3.375rem;--pf-global--FontFamily--sans-serif:"RedHatText","Overpass",overpass,helvetica,arial,sans-serif;--pf-global--FontFamily--heading--sans-serif:"RedHatDisplay","Overpass",overpass,helvetica,arial,sans-serif;--pf-global--FontFamily--monospace:"Liberation Mono",consolas,"SFMono-Regular",menlo,monaco,"Courier New",monospace;--pf-global--FontFamily--overpass--sans-serif:"overpass",overpass,"open sans",-apple-system,blinkmacsystemfont,"Segoe UI",roboto,"Helvetica Neue",arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--pf-global--FontFamily--overpass--monospace:"overpass-mono",overpass-mono,"SFMono-Regular",menlo,monaco,consolas,"Liberation Mono","Courier New",monospace;--pf-global--FontSize--4xl:2.25rem;--pf-global--FontSize--3xl:1.75rem;--pf-global--FontSize--2xl:1.5rem;--pf-global--FontSize--xl:1.25rem;--pf-global--FontSize--lg:1.125rem;--pf-global--FontSize--md:1rem;--pf-global--FontSize--sm:0.875rem;--pf-global--FontSize--xs:0.75rem;--pf-global--FontWeight--light:300;--pf-global--FontWeight--normal:400;--pf-global--FontWeight--semi-bold:700;--pf-global--FontWeight--overpass--semi-bold:500;--pf-global--FontWeight--bold:700;--pf-global--FontWeight--overpass--bold:600;--pf-global--LineHeight--sm:1.3;--pf-global--LineHeight--md:1.5;--pf-global--ListStyle:disc outside;--pf-global--Transition:all 250ms ease-in-out;--pf-global--TimingFunction:cubic-bezier(0.645,0.045,0.355,1);--pf-global--TransitionDuration:250ms;--pf-global--arrow--width:0.9375rem;--pf-global--arrow--width-lg:1.5625rem;--pf-global--target-size--MinWidth:44px;--pf-global--target-size--MinHeight:44px}.pf-m-overpass-font{--pf-global--FontFamily--sans-serif:var(--pf-global--FontFamily--overpass--sans-serif);--pf-global--FontFamily--heading--sans-serif:var(--pf-global--FontFamily--sans-serif);--pf-global--FontFamily--monospace:var(--pf-global--FontFamily--overpass--monospace);--pf-global--FontWeight--semi-bold:var(--pf-global--FontWeight--overpass--semi-bold);--pf-global--FontWeight--bold:var(--pf-global--FontWeight--overpass--bold)}@font-face{font-family:RedHatDisplay;src:url(assets/fonts/RedHatDisplay/RedHatDisplay-Regular.eot);src:url(assets/fonts/RedHatDisplay/RedHatDisplay-Regular.eot?#iefix) format("embedded-opentype"),url(assets/fonts/RedHatDisplay/RedHatDisplay-Regular.woff) format("woff");font-style:normal;font-weight:300;text-rendering:optimizeLegibility}@font-face{font-family:RedHatDisplay;src:url(assets/fonts/RedHatDisplay/RedHatDisplay-Medium.eot);src:url(assets/fonts/RedHatDisplay/RedHatDisplay-Medium.eot?#iefix) format("embedded-opentype"),url(assets/fonts/RedHatDisplay/RedHatDisplay-Medium.woff) format("woff");font-style:normal;font-weight:400;text-rendering:optimizeLegibility}@font-face{font-family:RedHatDisplay;src:url(assets/fonts/RedHatDisplay/RedHatDisplay-Bold.eot);src:url(assets/fonts/RedHatDisplay/RedHatDisplay-Bold.eot?#iefix) format("embedded-opentype"),url(assets/fonts/RedHatDisplay/RedHatDisplay-Bold.woff) format("woff");font-style:normal;font-weight:700;text-rendering:optimizeLegibility}@font-face{font-family:RedHatText;src:url(assets/fonts/RedHatText/RedHatText-Regular.eot);src:url(assets/fonts/RedHatText/RedHatText-Regular.eot?#iefix) format("embedded-opentype"),url(assets/fonts/RedHatText/RedHatText-Regular.woff) format("woff");font-style:normal;font-weight:400;text-rendering:optimizeLegibility}@font-face{font-family:RedHatText;src:url(assets/fonts/RedHatText/RedHatText-Medium.eot);src:url(assets/fonts/RedHatText/RedHatText-Medium.eot?#iefix) format("embedded-opentype"),url(assets/fonts/RedHatText/RedHatText-Medium.woff) format("woff");font-style:normal;font-weight:700;text-rendering:optimizeLegibility}@font-face{font-family:overpass;font-style:normal;font-weight:200;src:url(assets/fonts/overpass-webfont/overpass-thin.eot);src:url(assets/fonts/overpass-webfont/overpass-thin.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-thin.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-thin.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-thin.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:200;src:url(assets/fonts/overpass-webfont/overpass-thin-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-thin-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-thin-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-thin-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-thin-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:300;src:url(assets/fonts/overpass-webfont/overpass-extralight.eot);src:url(assets/fonts/overpass-webfont/overpass-extralight.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-extralight.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-extralight.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-extralight.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:300;src:url(assets/fonts/overpass-webfont/overpass-extralight-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-extralight-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-extralight-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-extralight-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-extralight-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:400;src:url(assets/fonts/overpass-webfont/overpass-light.eot);src:url(assets/fonts/overpass-webfont/overpass-light.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-light.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-light.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-light.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:400;src:url(assets/fonts/overpass-webfont/overpass-light-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-light-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-light-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-light-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-light-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:500;src:url(assets/fonts/overpass-webfont/overpass-regular.eot);src:url(assets/fonts/overpass-webfont/overpass-regular.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-regular.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-regular.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-regular.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:500;src:url(assets/fonts/overpass-webfont/overpass-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:600;src:url(assets/fonts/overpass-webfont/overpass-semibold.eot);src:url(assets/fonts/overpass-webfont/overpass-semibold.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-semibold.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-semibold.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-semibold.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:600;src:url(assets/fonts/overpass-webfont/overpass-semibold-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-semibold-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-semibold-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-semibold-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-semibold-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:700;src:url(assets/fonts/overpass-webfont/overpass-bold.eot);src:url(assets/fonts/overpass-webfont/overpass-bold.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-bold.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-bold.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-bold.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:700;src:url(assets/fonts/overpass-webfont/overpass-bold-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-bold-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-bold-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-bold-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-bold-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:800;src:url(assets/fonts/overpass-webfont/overpass-extrabold.eot);src:url(assets/fonts/overpass-webfont/overpass-extrabold.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-extrabold.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-extrabold.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-extrabold.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:800;src:url(assets/fonts/overpass-webfont/overpass-extrabold-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-extrabold-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-extrabold-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-extrabold-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-extrabold-italic.ttf) format("truetype")}@font-face{font-family:overpass;font-style:normal;font-weight:900;src:url(assets/fonts/overpass-webfont/overpass-heavy.eot);src:url(assets/fonts/overpass-webfont/overpass-heavy.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-heavy.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-heavy.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-heavy.ttf) format("truetype")}@font-face{font-family:overpass;font-style:italic;font-weight:900;src:url(assets/fonts/overpass-webfont/overpass-heavy-italic.eot);src:url(assets/fonts/overpass-webfont/overpass-heavy-italic.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-webfont/overpass-heavy-italic.woff2) format("woff2"),url(assets/fonts/overpass-webfont/overpass-heavy-italic.woff) format("woff"),url(assets/fonts/overpass-webfont/overpass-heavy-italic.ttf) format("truetype")}@font-face{font-family:overpass-mono;font-style:normal;font-weight:300;src:url(assets/fonts/overpass-mono-webfont/overpass-mono-light.eot);src:url(assets/fonts/overpass-mono-webfont/overpass-mono-light.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-mono-webfont/overpass-mono-light.woff2) format("woff2"),url(assets/fonts/overpass-mono-webfont/overpass-mono-light.woff) format("woff"),url(assets/fonts/overpass-mono-webfont/overpass-mono-light.ttf) format("truetype")}@font-face{font-family:overpass-mono;font-style:normal;font-weight:400;src:url(assets/fonts/overpass-mono-webfont/overpass-mono-regular.eot);src:url(assets/fonts/overpass-mono-webfont/overpass-mono-regular.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-mono-webfont/overpass-mono-regular.woff2) format("woff2"),url(assets/fonts/overpass-mono-webfont/overpass-mono-regular.woff) format("woff"),url(assets/fonts/overpass-mono-webfont/overpass-mono-regular.ttf) format("truetype")}@font-face{font-family:overpass-mono;font-style:normal;font-weight:500;src:url(assets/fonts/overpass-mono-webfont/overpass-mono-semibold.eot);src:url(assets/fonts/overpass-mono-webfont/overpass-mono-semibold.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-mono-webfont/overpass-mono-semibold.woff2) format("woff2"),url(assets/fonts/overpass-mono-webfont/overpass-mono-semibold.woff) format("woff"),url(assets/fonts/overpass-mono-webfont/overpass-mono-semibold.ttf) format("truetype")}@font-face{font-family:overpass-mono;font-style:normal;font-weight:600;src:url(assets/fonts/overpass-mono-webfont/overpass-mono-bold.eot);src:url(assets/fonts/overpass-mono-webfont/overpass-mono-bold.eot?#iefix) format("embedded-opentype"),url(assets/fonts/overpass-mono-webfont/overpass-mono-bold.woff2) format("woff2"),url(assets/fonts/overpass-mono-webfont/overpass-mono-bold.woff) format("woff"),url(assets/fonts/overpass-mono-webfont/overpass-mono-bold.ttf) format("truetype")}[class*=pf-c-],[class*=pf-c-]:after,[class*=pf-c-]:before{padding:0;margin:0;background-color:transparent}html{font-size:unset!important}.pf-screen-reader{position:fixed;top:0;left:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{padding:0;margin:0}body,html{height:100%}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:var(--pf-global--FontWeight--normal)}ul{list-style:none}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:var(--pf-global--LineHeight--md);color:var(--pf-global--Color--100)}audio,embed,iframe,img,object,video{max-width:100%;height:auto}iframe{border:0}table{border-spacing:0;border-collapse:collapse}td,th{padding:0;text-align:left}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15}body{font-family:var(--pf-global--FontFamily--sans-serif);font-size:var(--pf-global--FontSize--md);font-weight:var(--pf-global--FontWeight--normal);line-height:var(--pf-global--LineHeight--md);text-align:left;background-color:var(--pf-global--BackgroundColor--100)}a{font-weight:var(--pf-global--link--FontWeight);color:var(--pf-global--link--Color);text-decoration:var(--pf-global--link--TextDecoration)}a:hover{--pf-global--link--Color:var(--pf-global--link--Color--hover);--pf-global--link--TextDecoration:var(--pf-global--link--TextDecoration--hover)}a,button{cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}.pf-m-overpass-font a{font-weight:var(--pf-global--FontWeight--semi-bold)}.pf-t-dark.pf-m-transparent{background-color:transparent}.pf-t-dark.pf-m-transparent-100{background-color:rgba(3,3,3,.42)}.pf-t-dark.pf-m-transparent-200{background-color:rgba(3,3,3,.6)}.pf-t-dark.pf-m-opaque-100{background-color:#3c3f42}.pf-t-dark.pf-m-opaque-200{background-color:#151515}.pf-t-light.pf-m-transparent{background-color:transparent}.pf-t-light.pf-m-opaque-100{background-color:#fff}.pf-t-light.pf-m-opaque-200{background-color:#fafafa}.pf-t-light.pf-m-opaque-300{background-color:#f0f0f0}* .fa,* .fab,* .fal,* .far,* .fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}* .fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}* .fa-xs{font-size:.75em}* .fa-sm{font-size:.875em}* .fa-1x{font-size:1em}* .fa-2x{font-size:2em}* .fa-3x{font-size:3em}* .fa-4x{font-size:4em}* .fa-5x{font-size:5em}* .fa-6x{font-size:6em}* .fa-7x{font-size:7em}* .fa-8x{font-size:8em}* .fa-9x{font-size:9em}* .fa-10x{font-size:10em}* .fa-fw{text-align:center;width:1.25em}* .fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}* .fa-ul>li{position:relative}* .fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}* .fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}* .fa-pull-left{float:left}* .fa-pull-right{float:right}* .fa.fa-pull-left,* .fab.fa-pull-left,* .fal.fa-pull-left,* .far.fa-pull-left,* .fas.fa-pull-left{margin-right:.3em}* .fa.fa-pull-right,* .fab.fa-pull-right,* .fal.fa-pull-right,* .far.fa-pull-right,* .fas.fa-pull-right{margin-left:.3em}* .fa-spin{animation:fa-spin 2s linear infinite}* .fa-pulse{animation:fa-spin 1s steps(8) infinite}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}* .fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}* .fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}* .fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}* .fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}* .fa-flip-vertical{transform:scaleY(-1)}* .fa-flip-horizontal.fa-flip-vertical,* .fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}* .fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}* :root .fa-flip-horizontal,* :root .fa-flip-vertical,* :root .fa-rotate-90,* :root .fa-rotate-180,* :root .fa-rotate-270{filter:none}* .fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}* .fa-stack-1x,* .fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}* .fa-stack-1x{line-height:inherit}* .fa-stack-2x{font-size:2em}* .fa-inverse{color:#fff}* .fa-500px:before{content:"\f26e"}* .fa-accessible-icon:before{content:"\f368"}* .fa-accusoft:before{content:"\f369"}* .fa-acquisitions-incorporated:before{content:"\f6af"}* .fa-ad:before{content:"\f641"}* .fa-address-book:before{content:"\f2b9"}* .fa-address-card:before{content:"\f2bb"}* .fa-adjust:before{content:"\f042"}* .fa-adn:before{content:"\f170"}* .fa-adobe:before{content:"\f778"}* .fa-adversal:before{content:"\f36a"}* .fa-affiliatetheme:before{content:"\f36b"}* .fa-air-freshener:before{content:"\f5d0"}* .fa-algolia:before{content:"\f36c"}* .fa-align-center:before{content:"\f037"}* .fa-align-justify:before{content:"\f039"}* .fa-align-left:before{content:"\f036"}* .fa-align-right:before{content:"\f038"}* .fa-alipay:before{content:"\f642"}* .fa-allergies:before{content:"\f461"}* .fa-amazon:before{content:"\f270"}* .fa-amazon-pay:before{content:"\f42c"}* .fa-ambulance:before{content:"\f0f9"}* .fa-american-sign-language-interpreting:before{content:"\f2a3"}* .fa-amilia:before{content:"\f36d"}* .fa-anchor:before{content:"\f13d"}* .fa-android:before{content:"\f17b"}* .fa-angellist:before{content:"\f209"}* .fa-angle-double-down:before{content:"\f103"}* .fa-angle-double-left:before{content:"\f100"}* .fa-angle-double-right:before{content:"\f101"}* .fa-angle-double-up:before{content:"\f102"}* .fa-angle-down:before{content:"\f107"}* .fa-angle-left:before{content:"\f104"}* .fa-angle-right:before{content:"\f105"}* .fa-angle-up:before{content:"\f106"}* .fa-angry:before{content:"\f556"}* .fa-angrycreative:before{content:"\f36e"}* .fa-angular:before{content:"\f420"}* .fa-ankh:before{content:"\f644"}* .fa-app-store:before{content:"\f36f"}* .fa-app-store-ios:before{content:"\f370"}* .fa-apper:before{content:"\f371"}* .fa-apple:before{content:"\f179"}* .fa-apple-alt:before{content:"\f5d1"}* .fa-apple-pay:before{content:"\f415"}* .fa-archive:before{content:"\f187"}* .fa-archway:before{content:"\f557"}* .fa-arrow-alt-circle-down:before{content:"\f358"}* .fa-arrow-alt-circle-left:before{content:"\f359"}* .fa-arrow-alt-circle-right:before{content:"\f35a"}* .fa-arrow-alt-circle-up:before{content:"\f35b"}* .fa-arrow-circle-down:before{content:"\f0ab"}* .fa-arrow-circle-left:before{content:"\f0a8"}* .fa-arrow-circle-right:before{content:"\f0a9"}* .fa-arrow-circle-up:before{content:"\f0aa"}* .fa-arrow-down:before{content:"\f063"}* .fa-arrow-left:before{content:"\f060"}* .fa-arrow-right:before{content:"\f061"}* .fa-arrow-up:before{content:"\f062"}* .fa-arrows-alt:before{content:"\f0b2"}* .fa-arrows-alt-h:before{content:"\f337"}* .fa-arrows-alt-v:before{content:"\f338"}* .fa-artstation:before{content:"\f77a"}* .fa-assistive-listening-systems:before{content:"\f2a2"}* .fa-asterisk:before{content:"\f069"}* .fa-asymmetrik:before{content:"\f372"}* .fa-at:before{content:"\f1fa"}* .fa-atlas:before{content:"\f558"}* .fa-atlassian:before{content:"\f77b"}* .fa-atom:before{content:"\f5d2"}* .fa-audible:before{content:"\f373"}* .fa-audio-description:before{content:"\f29e"}* .fa-autoprefixer:before{content:"\f41c"}* .fa-avianex:before{content:"\f374"}* .fa-aviato:before{content:"\f421"}* .fa-award:before{content:"\f559"}* .fa-aws:before{content:"\f375"}* .fa-baby:before{content:"\f77c"}* .fa-baby-carriage:before{content:"\f77d"}* .fa-backspace:before{content:"\f55a"}* .fa-backward:before{content:"\f04a"}* .fa-balance-scale:before{content:"\f24e"}* .fa-ban:before{content:"\f05e"}* .fa-band-aid:before{content:"\f462"}* .fa-bandcamp:before{content:"\f2d5"}* .fa-barcode:before{content:"\f02a"}* .fa-bars:before{content:"\f0c9"}* .fa-baseball-ball:before{content:"\f433"}* .fa-basketball-ball:before{content:"\f434"}* .fa-bath:before{content:"\f2cd"}* .fa-battery-empty:before{content:"\f244"}* .fa-battery-full:before{content:"\f240"}* .fa-battery-half:before{content:"\f242"}* .fa-battery-quarter:before{content:"\f243"}* .fa-battery-three-quarters:before{content:"\f241"}* .fa-bed:before{content:"\f236"}* .fa-beer:before{content:"\f0fc"}* .fa-behance:before{content:"\f1b4"}* .fa-behance-square:before{content:"\f1b5"}* .fa-bell:before{content:"\f0f3"}* .fa-bell-slash:before{content:"\f1f6"}* .fa-bezier-curve:before{content:"\f55b"}* .fa-bible:before{content:"\f647"}* .fa-bicycle:before{content:"\f206"}* .fa-bimobject:before{content:"\f378"}* .fa-binoculars:before{content:"\f1e5"}* .fa-biohazard:before{content:"\f780"}* .fa-birthday-cake:before{content:"\f1fd"}* .fa-bitbucket:before{content:"\f171"}* .fa-bitcoin:before{content:"\f379"}* .fa-bity:before{content:"\f37a"}* .fa-black-tie:before{content:"\f27e"}* .fa-blackberry:before{content:"\f37b"}* .fa-blender:before{content:"\f517"}* .fa-blender-phone:before{content:"\f6b6"}* .fa-blind:before{content:"\f29d"}* .fa-blog:before{content:"\f781"}* .fa-blogger:before{content:"\f37c"}* .fa-blogger-b:before{content:"\f37d"}* .fa-bluetooth:before{content:"\f293"}* .fa-bluetooth-b:before{content:"\f294"}* .fa-bold:before{content:"\f032"}* .fa-bolt:before{content:"\f0e7"}* .fa-bomb:before{content:"\f1e2"}* .fa-bone:before{content:"\f5d7"}* .fa-bong:before{content:"\f55c"}* .fa-book:before{content:"\f02d"}* .fa-book-dead:before{content:"\f6b7"}* .fa-book-open:before{content:"\f518"}* .fa-book-reader:before{content:"\f5da"}* .fa-bookmark:before{content:"\f02e"}* .fa-bowling-ball:before{content:"\f436"}* .fa-box:before{content:"\f466"}* .fa-box-open:before{content:"\f49e"}* .fa-boxes:before{content:"\f468"}* .fa-braille:before{content:"\f2a1"}* .fa-brain:before{content:"\f5dc"}* .fa-briefcase:before{content:"\f0b1"}* .fa-briefcase-medical:before{content:"\f469"}* .fa-broadcast-tower:before{content:"\f519"}* .fa-broom:before{content:"\f51a"}* .fa-brush:before{content:"\f55d"}* .fa-btc:before{content:"\f15a"}* .fa-bug:before{content:"\f188"}* .fa-building:before{content:"\f1ad"}* .fa-bullhorn:before{content:"\f0a1"}* .fa-bullseye:before{content:"\f140"}* .fa-burn:before{content:"\f46a"}* .fa-buromobelexperte:before{content:"\f37f"}* .fa-bus:before{content:"\f207"}* .fa-bus-alt:before{content:"\f55e"}* .fa-business-time:before{content:"\f64a"}* .fa-buysellads:before{content:"\f20d"}* .fa-calculator:before{content:"\f1ec"}* .fa-calendar:before{content:"\f133"}* .fa-calendar-alt:before{content:"\f073"}* .fa-calendar-check:before{content:"\f274"}* .fa-calendar-day:before{content:"\f783"}* .fa-calendar-minus:before{content:"\f272"}* .fa-calendar-plus:before{content:"\f271"}* .fa-calendar-times:before{content:"\f273"}* .fa-calendar-week:before{content:"\f784"}* .fa-camera:before{content:"\f030"}* .fa-camera-retro:before{content:"\f083"}* .fa-campground:before{content:"\f6bb"}* .fa-canadian-maple-leaf:before{content:"\f785"}* .fa-candy-cane:before{content:"\f786"}* .fa-cannabis:before{content:"\f55f"}* .fa-capsules:before{content:"\f46b"}* .fa-car:before{content:"\f1b9"}* .fa-car-alt:before{content:"\f5de"}* .fa-car-battery:before{content:"\f5df"}* .fa-car-crash:before{content:"\f5e1"}* .fa-car-side:before{content:"\f5e4"}* .fa-caret-down:before{content:"\f0d7"}* .fa-caret-left:before{content:"\f0d9"}* .fa-caret-right:before{content:"\f0da"}* .fa-caret-square-down:before{content:"\f150"}* .fa-caret-square-left:before{content:"\f191"}* .fa-caret-square-right:before{content:"\f152"}* .fa-caret-square-up:before{content:"\f151"}* .fa-caret-up:before{content:"\f0d8"}* .fa-carrot:before{content:"\f787"}* .fa-cart-arrow-down:before{content:"\f218"}* .fa-cart-plus:before{content:"\f217"}* .fa-cash-register:before{content:"\f788"}* .fa-cat:before{content:"\f6be"}* .fa-cc-amazon-pay:before{content:"\f42d"}* .fa-cc-amex:before{content:"\f1f3"}* .fa-cc-apple-pay:before{content:"\f416"}* .fa-cc-diners-club:before{content:"\f24c"}* .fa-cc-discover:before{content:"\f1f2"}* .fa-cc-jcb:before{content:"\f24b"}* .fa-cc-mastercard:before{content:"\f1f1"}* .fa-cc-paypal:before{content:"\f1f4"}* .fa-cc-stripe:before{content:"\f1f5"}* .fa-cc-visa:before{content:"\f1f0"}* .fa-centercode:before{content:"\f380"}* .fa-centos:before{content:"\f789"}* .fa-certificate:before{content:"\f0a3"}* .fa-chair:before{content:"\f6c0"}* .fa-chalkboard:before{content:"\f51b"}* .fa-chalkboard-teacher:before{content:"\f51c"}* .fa-charging-station:before{content:"\f5e7"}* .fa-chart-area:before{content:"\f1fe"}* .fa-chart-bar:before{content:"\f080"}* .fa-chart-line:before{content:"\f201"}* .fa-chart-pie:before{content:"\f200"}* .fa-check:before{content:"\f00c"}* .fa-check-circle:before{content:"\f058"}* .fa-check-double:before{content:"\f560"}* .fa-check-square:before{content:"\f14a"}* .fa-chess:before{content:"\f439"}* .fa-chess-bishop:before{content:"\f43a"}* .fa-chess-board:before{content:"\f43c"}* .fa-chess-king:before{content:"\f43f"}* .fa-chess-knight:before{content:"\f441"}* .fa-chess-pawn:before{content:"\f443"}* .fa-chess-queen:before{content:"\f445"}* .fa-chess-rook:before{content:"\f447"}* .fa-chevron-circle-down:before{content:"\f13a"}* .fa-chevron-circle-left:before{content:"\f137"}* .fa-chevron-circle-right:before{content:"\f138"}* .fa-chevron-circle-up:before{content:"\f139"}* .fa-chevron-down:before{content:"\f078"}* .fa-chevron-left:before{content:"\f053"}* .fa-chevron-right:before{content:"\f054"}* .fa-chevron-up:before{content:"\f077"}* .fa-child:before{content:"\f1ae"}* .fa-chrome:before{content:"\f268"}* .fa-church:before{content:"\f51d"}* .fa-circle:before{content:"\f111"}* .fa-circle-notch:before{content:"\f1ce"}* .fa-city:before{content:"\f64f"}* .fa-clipboard:before{content:"\f328"}* .fa-clipboard-check:before{content:"\f46c"}* .fa-clipboard-list:before{content:"\f46d"}* .fa-clock:before{content:"\f017"}* .fa-clone:before{content:"\f24d"}* .fa-closed-captioning:before{content:"\f20a"}* .fa-cloud:before{content:"\f0c2"}* .fa-cloud-download-alt:before{content:"\f381"}* .fa-cloud-meatball:before{content:"\f73b"}* .fa-cloud-moon:before{content:"\f6c3"}* .fa-cloud-moon-rain:before{content:"\f73c"}* .fa-cloud-rain:before{content:"\f73d"}* .fa-cloud-showers-heavy:before{content:"\f740"}* .fa-cloud-sun:before{content:"\f6c4"}* .fa-cloud-sun-rain:before{content:"\f743"}* .fa-cloud-upload-alt:before{content:"\f382"}* .fa-cloudscale:before{content:"\f383"}* .fa-cloudsmith:before{content:"\f384"}* .fa-cloudversify:before{content:"\f385"}* .fa-cocktail:before{content:"\f561"}* .fa-code:before{content:"\f121"}* .fa-code-branch:before{content:"\f126"}* .fa-codepen:before{content:"\f1cb"}* .fa-codiepie:before{content:"\f284"}* .fa-coffee:before{content:"\f0f4"}* .fa-cog:before{content:"\f013"}* .fa-cogs:before{content:"\f085"}* .fa-coins:before{content:"\f51e"}* .fa-columns:before{content:"\f0db"}* .fa-comment:before{content:"\f075"}* .fa-comment-alt:before{content:"\f27a"}* .fa-comment-dollar:before{content:"\f651"}* .fa-comment-dots:before{content:"\f4ad"}* .fa-comment-slash:before{content:"\f4b3"}* .fa-comments:before{content:"\f086"}* .fa-comments-dollar:before{content:"\f653"}* .fa-compact-disc:before{content:"\f51f"}* .fa-compass:before{content:"\f14e"}* .fa-compress:before{content:"\f066"}* .fa-compress-arrows-alt:before{content:"\f78c"}* .fa-concierge-bell:before{content:"\f562"}* .fa-confluence:before{content:"\f78d"}* .fa-connectdevelop:before{content:"\f20e"}* .fa-contao:before{content:"\f26d"}* .fa-cookie:before{content:"\f563"}* .fa-cookie-bite:before{content:"\f564"}* .fa-copy:before{content:"\f0c5"}* .fa-copyright:before{content:"\f1f9"}* .fa-couch:before{content:"\f4b8"}* .fa-cpanel:before{content:"\f388"}* .fa-creative-commons:before{content:"\f25e"}* .fa-creative-commons-by:before{content:"\f4e7"}* .fa-creative-commons-nc:before{content:"\f4e8"}* .fa-creative-commons-nc-eu:before{content:"\f4e9"}* .fa-creative-commons-nc-jp:before{content:"\f4ea"}* .fa-creative-commons-nd:before{content:"\f4eb"}* .fa-creative-commons-pd:before{content:"\f4ec"}* .fa-creative-commons-pd-alt:before{content:"\f4ed"}* .fa-creative-commons-remix:before{content:"\f4ee"}* .fa-creative-commons-sa:before{content:"\f4ef"}* .fa-creative-commons-sampling:before{content:"\f4f0"}* .fa-creative-commons-sampling-plus:before{content:"\f4f1"}* .fa-creative-commons-share:before{content:"\f4f2"}* .fa-creative-commons-zero:before{content:"\f4f3"}* .fa-credit-card:before{content:"\f09d"}* .fa-critical-role:before{content:"\f6c9"}* .fa-crop:before{content:"\f125"}* .fa-crop-alt:before{content:"\f565"}* .fa-cross:before{content:"\f654"}* .fa-crosshairs:before{content:"\f05b"}* .fa-crow:before{content:"\f520"}* .fa-crown:before{content:"\f521"}* .fa-css3:before{content:"\f13c"}* .fa-css3-alt:before{content:"\f38b"}* .fa-cube:before{content:"\f1b2"}* .fa-cubes:before{content:"\f1b3"}* .fa-cut:before{content:"\f0c4"}* .fa-cuttlefish:before{content:"\f38c"}* .fa-d-and-d:before{content:"\f38d"}* .fa-d-and-d-beyond:before{content:"\f6ca"}* .fa-dashcube:before{content:"\f210"}* .fa-database:before{content:"\f1c0"}* .fa-deaf:before{content:"\f2a4"}* .fa-delicious:before{content:"\f1a5"}* .fa-democrat:before{content:"\f747"}* .fa-deploydog:before{content:"\f38e"}* .fa-deskpro:before{content:"\f38f"}* .fa-desktop:before{content:"\f108"}* .fa-dev:before{content:"\f6cc"}* .fa-deviantart:before{content:"\f1bd"}* .fa-dharmachakra:before{content:"\f655"}* .fa-dhl:before{content:"\f790"}* .fa-diagnoses:before{content:"\f470"}* .fa-diaspora:before{content:"\f791"}* .fa-dice:before{content:"\f522"}* .fa-dice-d20:before{content:"\f6cf"}* .fa-dice-d6:before{content:"\f6d1"}* .fa-dice-five:before{content:"\f523"}* .fa-dice-four:before{content:"\f524"}* .fa-dice-one:before{content:"\f525"}* .fa-dice-six:before{content:"\f526"}* .fa-dice-three:before{content:"\f527"}* .fa-dice-two:before{content:"\f528"}* .fa-digg:before{content:"\f1a6"}* .fa-digital-ocean:before{content:"\f391"}* .fa-digital-tachograph:before{content:"\f566"}* .fa-directions:before{content:"\f5eb"}* .fa-discord:before{content:"\f392"}* .fa-discourse:before{content:"\f393"}* .fa-divide:before{content:"\f529"}* .fa-dizzy:before{content:"\f567"}* .fa-dna:before{content:"\f471"}* .fa-dochub:before{content:"\f394"}* .fa-docker:before{content:"\f395"}* .fa-dog:before{content:"\f6d3"}* .fa-dollar-sign:before{content:"\f155"}* .fa-dolly:before{content:"\f472"}* .fa-dolly-flatbed:before{content:"\f474"}* .fa-donate:before{content:"\f4b9"}* .fa-door-closed:before{content:"\f52a"}* .fa-door-open:before{content:"\f52b"}* .fa-dot-circle:before{content:"\f192"}* .fa-dove:before{content:"\f4ba"}* .fa-download:before{content:"\f019"}* .fa-draft2digital:before{content:"\f396"}* .fa-drafting-compass:before{content:"\f568"}* .fa-dragon:before{content:"\f6d5"}* .fa-draw-polygon:before{content:"\f5ee"}* .fa-dribbble:before{content:"\f17d"}* .fa-dribbble-square:before{content:"\f397"}* .fa-dropbox:before{content:"\f16b"}* .fa-drum:before{content:"\f569"}* .fa-drum-steelpan:before{content:"\f56a"}* .fa-drumstick-bite:before{content:"\f6d7"}* .fa-drupal:before{content:"\f1a9"}* .fa-dumbbell:before{content:"\f44b"}* .fa-dumpster:before{content:"\f793"}* .fa-dumpster-fire:before{content:"\f794"}* .fa-dungeon:before{content:"\f6d9"}* .fa-dyalog:before{content:"\f399"}* .fa-earlybirds:before{content:"\f39a"}* .fa-ebay:before{content:"\f4f4"}* .fa-edge:before{content:"\f282"}* .fa-edit:before{content:"\f044"}* .fa-eject:before{content:"\f052"}* .fa-elementor:before{content:"\f430"}* .fa-ellipsis-h:before{content:"\f141"}* .fa-ellipsis-v:before{content:"\f142"}* .fa-ello:before{content:"\f5f1"}* .fa-ember:before{content:"\f423"}* .fa-empire:before{content:"\f1d1"}* .fa-envelope:before{content:"\f0e0"}* .fa-envelope-open:before{content:"\f2b6"}* .fa-envelope-open-text:before{content:"\f658"}* .fa-envelope-square:before{content:"\f199"}* .fa-envira:before{content:"\f299"}* .fa-equals:before{content:"\f52c"}* .fa-eraser:before{content:"\f12d"}* .fa-erlang:before{content:"\f39d"}* .fa-ethereum:before{content:"\f42e"}* .fa-ethernet:before{content:"\f796"}* .fa-etsy:before{content:"\f2d7"}* .fa-euro-sign:before{content:"\f153"}* .fa-exchange-alt:before{content:"\f362"}* .fa-exclamation:before{content:"\f12a"}* .fa-exclamation-circle:before{content:"\f06a"}* .fa-exclamation-triangle:before{content:"\f071"}* .fa-expand:before{content:"\f065"}* .fa-expand-arrows-alt:before{content:"\f31e"}* .fa-expeditedssl:before{content:"\f23e"}* .fa-external-link-alt:before{content:"\f35d"}* .fa-external-link-square-alt:before{content:"\f360"}* .fa-eye:before{content:"\f06e"}* .fa-eye-dropper:before{content:"\f1fb"}* .fa-eye-slash:before{content:"\f070"}* .fa-facebook:before{content:"\f09a"}* .fa-facebook-f:before{content:"\f39e"}* .fa-facebook-messenger:before{content:"\f39f"}* .fa-facebook-square:before{content:"\f082"}* .fa-fantasy-flight-games:before{content:"\f6dc"}* .fa-fast-backward:before{content:"\f049"}* .fa-fast-forward:before{content:"\f050"}* .fa-fax:before{content:"\f1ac"}* .fa-feather:before{content:"\f52d"}* .fa-feather-alt:before{content:"\f56b"}* .fa-fedex:before{content:"\f797"}* .fa-fedora:before{content:"\f798"}* .fa-female:before{content:"\f182"}* .fa-fighter-jet:before{content:"\f0fb"}* .fa-figma:before{content:"\f799"}* .fa-file:before{content:"\f15b"}* .fa-file-alt:before{content:"\f15c"}* .fa-file-archive:before{content:"\f1c6"}* .fa-file-audio:before{content:"\f1c7"}* .fa-file-code:before{content:"\f1c9"}* .fa-file-contract:before{content:"\f56c"}* .fa-file-csv:before{content:"\f6dd"}* .fa-file-download:before{content:"\f56d"}* .fa-file-excel:before{content:"\f1c3"}* .fa-file-export:before{content:"\f56e"}* .fa-file-image:before{content:"\f1c5"}* .fa-file-import:before{content:"\f56f"}* .fa-file-invoice:before{content:"\f570"}* .fa-file-invoice-dollar:before{content:"\f571"}* .fa-file-medical:before{content:"\f477"}* .fa-file-medical-alt:before{content:"\f478"}* .fa-file-pdf:before{content:"\f1c1"}* .fa-file-powerpoint:before{content:"\f1c4"}* .fa-file-prescription:before{content:"\f572"}* .fa-file-signature:before{content:"\f573"}* .fa-file-upload:before{content:"\f574"}* .fa-file-video:before{content:"\f1c8"}* .fa-file-word:before{content:"\f1c2"}* .fa-fill:before{content:"\f575"}* .fa-fill-drip:before{content:"\f576"}* .fa-film:before{content:"\f008"}* .fa-filter:before{content:"\f0b0"}* .fa-fingerprint:before{content:"\f577"}* .fa-fire:before{content:"\f06d"}* .fa-fire-alt:before{content:"\f7e4"}* .fa-fire-extinguisher:before{content:"\f134"}* .fa-firefox:before{content:"\f269"}* .fa-first-aid:before{content:"\f479"}* .fa-first-order:before{content:"\f2b0"}* .fa-first-order-alt:before{content:"\f50a"}* .fa-firstdraft:before{content:"\f3a1"}* .fa-fish:before{content:"\f578"}* .fa-fist-raised:before{content:"\f6de"}* .fa-flag:before{content:"\f024"}* .fa-flag-checkered:before{content:"\f11e"}* .fa-flag-usa:before{content:"\f74d"}* .fa-flask:before{content:"\f0c3"}* .fa-flickr:before{content:"\f16e"}* .fa-flipboard:before{content:"\f44d"}* .fa-flushed:before{content:"\f579"}* .fa-fly:before{content:"\f417"}* .fa-folder:before{content:"\f07b"}* .fa-folder-minus:before{content:"\f65d"}* .fa-folder-open:before{content:"\f07c"}* .fa-folder-plus:before{content:"\f65e"}* .fa-font:before{content:"\f031"}* .fa-font-awesome:before{content:"\f2b4"}* .fa-font-awesome-alt:before{content:"\f35c"}* .fa-font-awesome-flag:before{content:"\f425"}* .fa-font-awesome-logo-full:before{content:"\f4e6"}* .fa-fonticons:before{content:"\f280"}* .fa-fonticons-fi:before{content:"\f3a2"}* .fa-football-ball:before{content:"\f44e"}* .fa-fort-awesome:before{content:"\f286"}* .fa-fort-awesome-alt:before{content:"\f3a3"}* .fa-forumbee:before{content:"\f211"}* .fa-forward:before{content:"\f04e"}* .fa-foursquare:before{content:"\f180"}* .fa-free-code-camp:before{content:"\f2c5"}* .fa-freebsd:before{content:"\f3a4"}* .fa-frog:before{content:"\f52e"}* .fa-frown:before{content:"\f119"}* .fa-frown-open:before{content:"\f57a"}* .fa-fulcrum:before{content:"\f50b"}* .fa-funnel-dollar:before{content:"\f662"}* .fa-futbol:before{content:"\f1e3"}* .fa-galactic-republic:before{content:"\f50c"}* .fa-galactic-senate:before{content:"\f50d"}* .fa-gamepad:before{content:"\f11b"}* .fa-gas-pump:before{content:"\f52f"}* .fa-gavel:before{content:"\f0e3"}* .fa-gem:before{content:"\f3a5"}* .fa-genderless:before{content:"\f22d"}* .fa-get-pocket:before{content:"\f265"}* .fa-gg:before{content:"\f260"}* .fa-gg-circle:before{content:"\f261"}* .fa-ghost:before{content:"\f6e2"}* .fa-gift:before{content:"\f06b"}* .fa-gifts:before{content:"\f79c"}* .fa-git:before{content:"\f1d3"}* .fa-git-square:before{content:"\f1d2"}* .fa-github:before{content:"\f09b"}* .fa-github-alt:before{content:"\f113"}* .fa-github-square:before{content:"\f092"}* .fa-gitkraken:before{content:"\f3a6"}* .fa-gitlab:before{content:"\f296"}* .fa-gitter:before{content:"\f426"}* .fa-glass-cheers:before{content:"\f79f"}* .fa-glass-martini:before{content:"\f000"}* .fa-glass-martini-alt:before{content:"\f57b"}* .fa-glass-whiskey:before{content:"\f7a0"}* .fa-glasses:before{content:"\f530"}* .fa-glide:before{content:"\f2a5"}* .fa-glide-g:before{content:"\f2a6"}* .fa-globe:before{content:"\f0ac"}* .fa-globe-africa:before{content:"\f57c"}* .fa-globe-americas:before{content:"\f57d"}* .fa-globe-asia:before{content:"\f57e"}* .fa-globe-europe:before{content:"\f7a2"}* .fa-gofore:before{content:"\f3a7"}* .fa-golf-ball:before{content:"\f450"}* .fa-goodreads:before{content:"\f3a8"}* .fa-goodreads-g:before{content:"\f3a9"}* .fa-google:before{content:"\f1a0"}* .fa-google-drive:before{content:"\f3aa"}* .fa-google-play:before{content:"\f3ab"}* .fa-google-plus:before{content:"\f2b3"}* .fa-google-plus-g:before{content:"\f0d5"}* .fa-google-plus-square:before{content:"\f0d4"}* .fa-google-wallet:before{content:"\f1ee"}* .fa-gopuram:before{content:"\f664"}* .fa-graduation-cap:before{content:"\f19d"}* .fa-gratipay:before{content:"\f184"}* .fa-grav:before{content:"\f2d6"}* .fa-greater-than:before{content:"\f531"}* .fa-greater-than-equal:before{content:"\f532"}* .fa-grimace:before{content:"\f57f"}* .fa-grin:before{content:"\f580"}* .fa-grin-alt:before{content:"\f581"}* .fa-grin-beam:before{content:"\f582"}* .fa-grin-beam-sweat:before{content:"\f583"}* .fa-grin-hearts:before{content:"\f584"}* .fa-grin-squint:before{content:"\f585"}* .fa-grin-squint-tears:before{content:"\f586"}* .fa-grin-stars:before{content:"\f587"}* .fa-grin-tears:before{content:"\f588"}* .fa-grin-tongue:before{content:"\f589"}* .fa-grin-tongue-squint:before{content:"\f58a"}* .fa-grin-tongue-wink:before{content:"\f58b"}* .fa-grin-wink:before{content:"\f58c"}* .fa-grip-horizontal:before{content:"\f58d"}* .fa-grip-lines:before{content:"\f7a4"}* .fa-grip-lines-vertical:before{content:"\f7a5"}* .fa-grip-vertical:before{content:"\f58e"}* .fa-gripfire:before{content:"\f3ac"}* .fa-grunt:before{content:"\f3ad"}* .fa-guitar:before{content:"\f7a6"}* .fa-gulp:before{content:"\f3ae"}* .fa-h-square:before{content:"\f0fd"}* .fa-hacker-news:before{content:"\f1d4"}* .fa-hacker-news-square:before{content:"\f3af"}* .fa-hackerrank:before{content:"\f5f7"}* .fa-hammer:before{content:"\f6e3"}* .fa-hamsa:before{content:"\f665"}* .fa-hand-holding:before{content:"\f4bd"}* .fa-hand-holding-heart:before{content:"\f4be"}* .fa-hand-holding-usd:before{content:"\f4c0"}* .fa-hand-lizard:before{content:"\f258"}* .fa-hand-paper:before{content:"\f256"}* .fa-hand-peace:before{content:"\f25b"}* .fa-hand-point-down:before{content:"\f0a7"}* .fa-hand-point-left:before{content:"\f0a5"}* .fa-hand-point-right:before{content:"\f0a4"}* .fa-hand-point-up:before{content:"\f0a6"}* .fa-hand-pointer:before{content:"\f25a"}* .fa-hand-rock:before{content:"\f255"}* .fa-hand-scissors:before{content:"\f257"}* .fa-hand-spock:before{content:"\f259"}* .fa-hands:before{content:"\f4c2"}* .fa-hands-helping:before{content:"\f4c4"}* .fa-handshake:before{content:"\f2b5"}* .fa-hanukiah:before{content:"\f6e6"}* .fa-hashtag:before{content:"\f292"}* .fa-hat-wizard:before{content:"\f6e8"}* .fa-haykal:before{content:"\f666"}* .fa-hdd:before{content:"\f0a0"}* .fa-heading:before{content:"\f1dc"}* .fa-headphones:before{content:"\f025"}* .fa-headphones-alt:before{content:"\f58f"}* .fa-headset:before{content:"\f590"}* .fa-heart:before{content:"\f004"}* .fa-heart-broken:before{content:"\f7a9"}* .fa-heartbeat:before{content:"\f21e"}* .fa-helicopter:before{content:"\f533"}* .fa-highlighter:before{content:"\f591"}* .fa-hiking:before{content:"\f6ec"}* .fa-hippo:before{content:"\f6ed"}* .fa-hips:before{content:"\f452"}* .fa-hire-a-helper:before{content:"\f3b0"}* .fa-history:before{content:"\f1da"}* .fa-hockey-puck:before{content:"\f453"}* .fa-holly-berry:before{content:"\f7aa"}* .fa-home:before{content:"\f015"}* .fa-hooli:before{content:"\f427"}* .fa-hornbill:before{content:"\f592"}* .fa-horse:before{content:"\f6f0"}* .fa-horse-head:before{content:"\f7ab"}* .fa-hospital:before{content:"\f0f8"}* .fa-hospital-alt:before{content:"\f47d"}* .fa-hospital-symbol:before{content:"\f47e"}* .fa-hot-tub:before{content:"\f593"}* .fa-hotel:before{content:"\f594"}* .fa-hotjar:before{content:"\f3b1"}* .fa-hourglass:before{content:"\f254"}* .fa-hourglass-end:before{content:"\f253"}* .fa-hourglass-half:before{content:"\f252"}* .fa-hourglass-start:before{content:"\f251"}* .fa-house-damage:before{content:"\f6f1"}* .fa-houzz:before{content:"\f27c"}* .fa-hryvnia:before{content:"\f6f2"}* .fa-html5:before{content:"\f13b"}* .fa-hubspot:before{content:"\f3b2"}* .fa-i-cursor:before{content:"\f246"}* .fa-icicles:before{content:"\f7ad"}* .fa-id-badge:before{content:"\f2c1"}* .fa-id-card:before{content:"\f2c2"}* .fa-id-card-alt:before{content:"\f47f"}* .fa-igloo:before{content:"\f7ae"}* .fa-image:before{content:"\f03e"}* .fa-images:before{content:"\f302"}* .fa-imdb:before{content:"\f2d8"}* .fa-inbox:before{content:"\f01c"}* .fa-indent:before{content:"\f03c"}* .fa-industry:before{content:"\f275"}* .fa-infinity:before{content:"\f534"}* .fa-info:before{content:"\f129"}* .fa-info-circle:before{content:"\f05a"}* .fa-instagram:before{content:"\f16d"}* .fa-intercom:before{content:"\f7af"}* .fa-internet-explorer:before{content:"\f26b"}* .fa-invision:before{content:"\f7b0"}* .fa-ioxhost:before{content:"\f208"}* .fa-italic:before{content:"\f033"}* .fa-itunes:before{content:"\f3b4"}* .fa-itunes-note:before{content:"\f3b5"}* .fa-java:before{content:"\f4e4"}* .fa-jedi:before{content:"\f669"}* .fa-jedi-order:before{content:"\f50e"}* .fa-jenkins:before{content:"\f3b6"}* .fa-jira:before{content:"\f7b1"}* .fa-joget:before{content:"\f3b7"}* .fa-joint:before{content:"\f595"}* .fa-joomla:before{content:"\f1aa"}* .fa-journal-whills:before{content:"\f66a"}* .fa-js:before{content:"\f3b8"}* .fa-js-square:before{content:"\f3b9"}* .fa-jsfiddle:before{content:"\f1cc"}* .fa-kaaba:before{content:"\f66b"}* .fa-kaggle:before{content:"\f5fa"}* .fa-key:before{content:"\f084"}* .fa-keybase:before{content:"\f4f5"}* .fa-keyboard:before{content:"\f11c"}* .fa-keycdn:before{content:"\f3ba"}* .fa-khanda:before{content:"\f66d"}* .fa-kickstarter:before{content:"\f3bb"}* .fa-kickstarter-k:before{content:"\f3bc"}* .fa-kiss:before{content:"\f596"}* .fa-kiss-beam:before{content:"\f597"}* .fa-kiss-wink-heart:before{content:"\f598"}* .fa-kiwi-bird:before{content:"\f535"}* .fa-korvue:before{content:"\f42f"}* .fa-landmark:before{content:"\f66f"}* .fa-language:before{content:"\f1ab"}* .fa-laptop:before{content:"\f109"}* .fa-laptop-code:before{content:"\f5fc"}* .fa-laravel:before{content:"\f3bd"}* .fa-lastfm:before{content:"\f202"}* .fa-lastfm-square:before{content:"\f203"}* .fa-laugh:before{content:"\f599"}* .fa-laugh-beam:before{content:"\f59a"}* .fa-laugh-squint:before{content:"\f59b"}* .fa-laugh-wink:before{content:"\f59c"}* .fa-layer-group:before{content:"\f5fd"}* .fa-leaf:before{content:"\f06c"}* .fa-leanpub:before{content:"\f212"}* .fa-lemon:before{content:"\f094"}* .fa-less:before{content:"\f41d"}* .fa-less-than:before{content:"\f536"}* .fa-less-than-equal:before{content:"\f537"}* .fa-level-down-alt:before{content:"\f3be"}* .fa-level-up-alt:before{content:"\f3bf"}* .fa-life-ring:before{content:"\f1cd"}* .fa-lightbulb:before{content:"\f0eb"}* .fa-line:before{content:"\f3c0"}* .fa-link:before{content:"\f0c1"}* .fa-linkedin:before{content:"\f08c"}* .fa-linkedin-in:before{content:"\f0e1"}* .fa-linode:before{content:"\f2b8"}* .fa-linux:before{content:"\f17c"}* .fa-lira-sign:before{content:"\f195"}* .fa-list:before{content:"\f03a"}* .fa-list-alt:before{content:"\f022"}* .fa-list-ol:before{content:"\f0cb"}* .fa-list-ul:before{content:"\f0ca"}* .fa-location-arrow:before{content:"\f124"}* .fa-lock:before{content:"\f023"}* .fa-lock-open:before{content:"\f3c1"}* .fa-long-arrow-alt-down:before{content:"\f309"}* .fa-long-arrow-alt-left:before{content:"\f30a"}* .fa-long-arrow-alt-right:before{content:"\f30b"}* .fa-long-arrow-alt-up:before{content:"\f30c"}* .fa-low-vision:before{content:"\f2a8"}* .fa-luggage-cart:before{content:"\f59d"}* .fa-lyft:before{content:"\f3c3"}* .fa-magento:before{content:"\f3c4"}* .fa-magic:before{content:"\f0d0"}* .fa-magnet:before{content:"\f076"}* .fa-mail-bulk:before{content:"\f674"}* .fa-mailchimp:before{content:"\f59e"}* .fa-male:before{content:"\f183"}* .fa-mandalorian:before{content:"\f50f"}* .fa-map:before{content:"\f279"}* .fa-map-marked:before{content:"\f59f"}* .fa-map-marked-alt:before{content:"\f5a0"}* .fa-map-marker:before{content:"\f041"}* .fa-map-marker-alt:before{content:"\f3c5"}* .fa-map-pin:before{content:"\f276"}* .fa-map-signs:before{content:"\f277"}* .fa-markdown:before{content:"\f60f"}* .fa-marker:before{content:"\f5a1"}* .fa-mars:before{content:"\f222"}* .fa-mars-double:before{content:"\f227"}* .fa-mars-stroke:before{content:"\f229"}* .fa-mars-stroke-h:before{content:"\f22b"}* .fa-mars-stroke-v:before{content:"\f22a"}* .fa-mask:before{content:"\f6fa"}* .fa-mastodon:before{content:"\f4f6"}* .fa-maxcdn:before{content:"\f136"}* .fa-medal:before{content:"\f5a2"}* .fa-medapps:before{content:"\f3c6"}* .fa-medium:before{content:"\f23a"}* .fa-medium-m:before{content:"\f3c7"}* .fa-medkit:before{content:"\f0fa"}* .fa-medrt:before{content:"\f3c8"}* .fa-meetup:before{content:"\f2e0"}* .fa-megaport:before{content:"\f5a3"}* .fa-meh:before{content:"\f11a"}* .fa-meh-blank:before{content:"\f5a4"}* .fa-meh-rolling-eyes:before{content:"\f5a5"}* .fa-memory:before{content:"\f538"}* .fa-mendeley:before{content:"\f7b3"}* .fa-menorah:before{content:"\f676"}* .fa-mercury:before{content:"\f223"}* .fa-meteor:before{content:"\f753"}* .fa-microchip:before{content:"\f2db"}* .fa-microphone:before{content:"\f130"}* .fa-microphone-alt:before{content:"\f3c9"}* .fa-microphone-alt-slash:before{content:"\f539"}* .fa-microphone-slash:before{content:"\f131"}* .fa-microscope:before{content:"\f610"}* .fa-microsoft:before{content:"\f3ca"}* .fa-minus:before{content:"\f068"}* .fa-minus-circle:before{content:"\f056"}* .fa-minus-square:before{content:"\f146"}* .fa-mitten:before{content:"\f7b5"}* .fa-mix:before{content:"\f3cb"}* .fa-mixcloud:before{content:"\f289"}* .fa-mizuni:before{content:"\f3cc"}* .fa-mobile:before{content:"\f10b"}* .fa-mobile-alt:before{content:"\f3cd"}* .fa-modx:before{content:"\f285"}* .fa-monero:before{content:"\f3d0"}* .fa-money-bill:before{content:"\f0d6"}* .fa-money-bill-alt:before{content:"\f3d1"}* .fa-money-bill-wave:before{content:"\f53a"}* .fa-money-bill-wave-alt:before{content:"\f53b"}* .fa-money-check:before{content:"\f53c"}* .fa-money-check-alt:before{content:"\f53d"}* .fa-monument:before{content:"\f5a6"}* .fa-moon:before{content:"\f186"}* .fa-mortar-pestle:before{content:"\f5a7"}* .fa-mosque:before{content:"\f678"}* .fa-motorcycle:before{content:"\f21c"}* .fa-mountain:before{content:"\f6fc"}* .fa-mouse-pointer:before{content:"\f245"}* .fa-mug-hot:before{content:"\f7b6"}* .fa-music:before{content:"\f001"}* .fa-napster:before{content:"\f3d2"}* .fa-neos:before{content:"\f612"}* .fa-network-wired:before{content:"\f6ff"}* .fa-neuter:before{content:"\f22c"}* .fa-newspaper:before{content:"\f1ea"}* .fa-nimblr:before{content:"\f5a8"}* .fa-nintendo-switch:before{content:"\f418"}* .fa-node:before{content:"\f419"}* .fa-node-js:before{content:"\f3d3"}* .fa-not-equal:before{content:"\f53e"}* .fa-notes-medical:before{content:"\f481"}* .fa-npm:before{content:"\f3d4"}* .fa-ns8:before{content:"\f3d5"}* .fa-nutritionix:before{content:"\f3d6"}* .fa-object-group:before{content:"\f247"}* .fa-object-ungroup:before{content:"\f248"}* .fa-odnoklassniki:before{content:"\f263"}* .fa-odnoklassniki-square:before{content:"\f264"}* .fa-oil-can:before{content:"\f613"}* .fa-old-republic:before{content:"\f510"}* .fa-om:before{content:"\f679"}* .fa-opencart:before{content:"\f23d"}* .fa-openid:before{content:"\f19b"}* .fa-opera:before{content:"\f26a"}* .fa-optin-monster:before{content:"\f23c"}* .fa-osi:before{content:"\f41a"}* .fa-otter:before{content:"\f700"}* .fa-outdent:before{content:"\f03b"}* .fa-page4:before{content:"\f3d7"}* .fa-pagelines:before{content:"\f18c"}* .fa-paint-brush:before{content:"\f1fc"}* .fa-paint-roller:before{content:"\f5aa"}* .fa-palette:before{content:"\f53f"}* .fa-palfed:before{content:"\f3d8"}* .fa-pallet:before{content:"\f482"}* .fa-paper-plane:before{content:"\f1d8"}* .fa-paperclip:before{content:"\f0c6"}* .fa-parachute-box:before{content:"\f4cd"}* .fa-paragraph:before{content:"\f1dd"}* .fa-parking:before{content:"\f540"}* .fa-passport:before{content:"\f5ab"}* .fa-pastafarianism:before{content:"\f67b"}* .fa-paste:before{content:"\f0ea"}* .fa-patreon:before{content:"\f3d9"}* .fa-pause:before{content:"\f04c"}* .fa-pause-circle:before{content:"\f28b"}* .fa-paw:before{content:"\f1b0"}* .fa-paypal:before{content:"\f1ed"}* .fa-peace:before{content:"\f67c"}* .fa-pen:before{content:"\f304"}* .fa-pen-alt:before{content:"\f305"}* .fa-pen-fancy:before{content:"\f5ac"}* .fa-pen-nib:before{content:"\f5ad"}* .fa-pen-square:before{content:"\f14b"}* .fa-pencil-alt:before{content:"\f303"}* .fa-pencil-ruler:before{content:"\f5ae"}* .fa-penny-arcade:before{content:"\f704"}* .fa-people-carry:before{content:"\f4ce"}* .fa-percent:before{content:"\f295"}* .fa-percentage:before{content:"\f541"}* .fa-periscope:before{content:"\f3da"}* .fa-person-booth:before{content:"\f756"}* .fa-phabricator:before{content:"\f3db"}* .fa-phoenix-framework:before{content:"\f3dc"}* .fa-phoenix-squadron:before{content:"\f511"}* .fa-phone:before{content:"\f095"}* .fa-phone-slash:before{content:"\f3dd"}* .fa-phone-square:before{content:"\f098"}* .fa-phone-volume:before{content:"\f2a0"}* .fa-php:before{content:"\f457"}* .fa-pied-piper:before{content:"\f2ae"}* .fa-pied-piper-alt:before{content:"\f1a8"}* .fa-pied-piper-hat:before{content:"\f4e5"}* .fa-pied-piper-pp:before{content:"\f1a7"}* .fa-piggy-bank:before{content:"\f4d3"}* .fa-pills:before{content:"\f484"}* .fa-pinterest:before{content:"\f0d2"}* .fa-pinterest-p:before{content:"\f231"}* .fa-pinterest-square:before{content:"\f0d3"}* .fa-place-of-worship:before{content:"\f67f"}* .fa-plane:before{content:"\f072"}* .fa-plane-arrival:before{content:"\f5af"}* .fa-plane-departure:before{content:"\f5b0"}* .fa-play:before{content:"\f04b"}* .fa-play-circle:before{content:"\f144"}* .fa-playstation:before{content:"\f3df"}* .fa-plug:before{content:"\f1e6"}* .fa-plus:before{content:"\f067"}* .fa-plus-circle:before{content:"\f055"}* .fa-plus-square:before{content:"\f0fe"}* .fa-podcast:before{content:"\f2ce"}* .fa-poll:before{content:"\f681"}* .fa-poll-h:before{content:"\f682"}* .fa-poo:before{content:"\f2fe"}* .fa-poo-storm:before{content:"\f75a"}* .fa-poop:before{content:"\f619"}* .fa-portrait:before{content:"\f3e0"}* .fa-pound-sign:before{content:"\f154"}* .fa-power-off:before{content:"\f011"}* .fa-pray:before{content:"\f683"}* .fa-praying-hands:before{content:"\f684"}* .fa-prescription:before{content:"\f5b1"}* .fa-prescription-bottle:before{content:"\f485"}* .fa-prescription-bottle-alt:before{content:"\f486"}* .fa-print:before{content:"\f02f"}* .fa-procedures:before{content:"\f487"}* .fa-product-hunt:before{content:"\f288"}* .fa-project-diagram:before{content:"\f542"}* .fa-pushed:before{content:"\f3e1"}* .fa-puzzle-piece:before{content:"\f12e"}* .fa-python:before{content:"\f3e2"}* .fa-qq:before{content:"\f1d6"}* .fa-qrcode:before{content:"\f029"}* .fa-question:before{content:"\f128"}* .fa-question-circle:before{content:"\f059"}* .fa-quidditch:before{content:"\f458"}* .fa-quinscape:before{content:"\f459"}* .fa-quora:before{content:"\f2c4"}* .fa-quote-left:before{content:"\f10d"}* .fa-quote-right:before{content:"\f10e"}* .fa-quran:before{content:"\f687"}* .fa-r-project:before{content:"\f4f7"}* .fa-radiation:before{content:"\f7b9"}* .fa-radiation-alt:before{content:"\f7ba"}* .fa-rainbow:before{content:"\f75b"}* .fa-random:before{content:"\f074"}* .fa-raspberry-pi:before{content:"\f7bb"}* .fa-ravelry:before{content:"\f2d9"}* .fa-react:before{content:"\f41b"}* .fa-reacteurope:before{content:"\f75d"}* .fa-readme:before{content:"\f4d5"}* .fa-rebel:before{content:"\f1d0"}* .fa-receipt:before{content:"\f543"}* .fa-recycle:before{content:"\f1b8"}* .fa-red-river:before{content:"\f3e3"}* .fa-reddit:before{content:"\f1a1"}* .fa-reddit-alien:before{content:"\f281"}* .fa-reddit-square:before{content:"\f1a2"}* .fa-redhat:before{content:"\f7bc"}* .fa-redo:before{content:"\f01e"}* .fa-redo-alt:before{content:"\f2f9"}* .fa-registered:before{content:"\f25d"}* .fa-renren:before{content:"\f18b"}* .fa-reply:before{content:"\f3e5"}* .fa-reply-all:before{content:"\f122"}* .fa-replyd:before{content:"\f3e6"}* .fa-republican:before{content:"\f75e"}* .fa-researchgate:before{content:"\f4f8"}* .fa-resolving:before{content:"\f3e7"}* .fa-restroom:before{content:"\f7bd"}* .fa-retweet:before{content:"\f079"}* .fa-rev:before{content:"\f5b2"}* .fa-ribbon:before{content:"\f4d6"}* .fa-ring:before{content:"\f70b"}* .fa-road:before{content:"\f018"}* .fa-robot:before{content:"\f544"}* .fa-rocket:before{content:"\f135"}* .fa-rocketchat:before{content:"\f3e8"}* .fa-rockrms:before{content:"\f3e9"}* .fa-route:before{content:"\f4d7"}* .fa-rss:before{content:"\f09e"}* .fa-rss-square:before{content:"\f143"}* .fa-ruble-sign:before{content:"\f158"}* .fa-ruler:before{content:"\f545"}* .fa-ruler-combined:before{content:"\f546"}* .fa-ruler-horizontal:before{content:"\f547"}* .fa-ruler-vertical:before{content:"\f548"}* .fa-running:before{content:"\f70c"}* .fa-rupee-sign:before{content:"\f156"}* .fa-sad-cry:before{content:"\f5b3"}* .fa-sad-tear:before{content:"\f5b4"}* .fa-safari:before{content:"\f267"}* .fa-sass:before{content:"\f41e"}* .fa-satellite:before{content:"\f7bf"}* .fa-satellite-dish:before{content:"\f7c0"}* .fa-save:before{content:"\f0c7"}* .fa-schlix:before{content:"\f3ea"}* .fa-school:before{content:"\f549"}* .fa-screwdriver:before{content:"\f54a"}* .fa-scribd:before{content:"\f28a"}* .fa-scroll:before{content:"\f70e"}* .fa-sd-card:before{content:"\f7c2"}* .fa-search:before{content:"\f002"}* .fa-search-dollar:before{content:"\f688"}* .fa-search-location:before{content:"\f689"}* .fa-search-minus:before{content:"\f010"}* .fa-search-plus:before{content:"\f00e"}* .fa-searchengin:before{content:"\f3eb"}* .fa-seedling:before{content:"\f4d8"}* .fa-sellcast:before{content:"\f2da"}* .fa-sellsy:before{content:"\f213"}* .fa-server:before{content:"\f233"}* .fa-servicestack:before{content:"\f3ec"}* .fa-shapes:before{content:"\f61f"}* .fa-share:before{content:"\f064"}* .fa-share-alt:before{content:"\f1e0"}* .fa-share-alt-square:before{content:"\f1e1"}* .fa-share-square:before{content:"\f14d"}* .fa-shekel-sign:before{content:"\f20b"}* .fa-shield-alt:before{content:"\f3ed"}* .fa-ship:before{content:"\f21a"}* .fa-shipping-fast:before{content:"\f48b"}* .fa-shirtsinbulk:before{content:"\f214"}* .fa-shoe-prints:before{content:"\f54b"}* .fa-shopping-bag:before{content:"\f290"}* .fa-shopping-basket:before{content:"\f291"}* .fa-shopping-cart:before{content:"\f07a"}* .fa-shopware:before{content:"\f5b5"}* .fa-shower:before{content:"\f2cc"}* .fa-shuttle-van:before{content:"\f5b6"}* .fa-sign:before{content:"\f4d9"}* .fa-sign-in-alt:before{content:"\f2f6"}* .fa-sign-language:before{content:"\f2a7"}* .fa-sign-out-alt:before{content:"\f2f5"}* .fa-signal:before{content:"\f012"}* .fa-signature:before{content:"\f5b7"}* .fa-sim-card:before{content:"\f7c4"}* .fa-simplybuilt:before{content:"\f215"}* .fa-sistrix:before{content:"\f3ee"}* .fa-sitemap:before{content:"\f0e8"}* .fa-sith:before{content:"\f512"}* .fa-skating:before{content:"\f7c5"}* .fa-sketch:before{content:"\f7c6"}* .fa-skiing:before{content:"\f7c9"}* .fa-skiing-nordic:before{content:"\f7ca"}* .fa-skull:before{content:"\f54c"}* .fa-skull-crossbones:before{content:"\f714"}* .fa-skyatlas:before{content:"\f216"}* .fa-skype:before{content:"\f17e"}* .fa-slack:before{content:"\f198"}* .fa-slack-hash:before{content:"\f3ef"}* .fa-slash:before{content:"\f715"}* .fa-sleigh:before{content:"\f7cc"}* .fa-sliders-h:before{content:"\f1de"}* .fa-slideshare:before{content:"\f1e7"}* .fa-smile:before{content:"\f118"}* .fa-smile-beam:before{content:"\f5b8"}* .fa-smile-wink:before{content:"\f4da"}* .fa-smog:before{content:"\f75f"}* .fa-smoking:before{content:"\f48d"}* .fa-smoking-ban:before{content:"\f54d"}* .fa-sms:before{content:"\f7cd"}* .fa-snapchat:before{content:"\f2ab"}* .fa-snapchat-ghost:before{content:"\f2ac"}* .fa-snapchat-square:before{content:"\f2ad"}* .fa-snowboarding:before{content:"\f7ce"}* .fa-snowflake:before{content:"\f2dc"}* .fa-snowman:before{content:"\f7d0"}* .fa-snowplow:before{content:"\f7d2"}* .fa-socks:before{content:"\f696"}* .fa-solar-panel:before{content:"\f5ba"}* .fa-sort:before{content:"\f0dc"}* .fa-sort-alpha-down:before{content:"\f15d"}* .fa-sort-alpha-up:before{content:"\f15e"}* .fa-sort-amount-down:before{content:"\f160"}* .fa-sort-amount-up:before{content:"\f161"}* .fa-sort-down:before{content:"\f0dd"}* .fa-sort-numeric-down:before{content:"\f162"}* .fa-sort-numeric-up:before{content:"\f163"}* .fa-sort-up:before{content:"\f0de"}* .fa-soundcloud:before{content:"\f1be"}* .fa-sourcetree:before{content:"\f7d3"}* .fa-spa:before{content:"\f5bb"}* .fa-space-shuttle:before{content:"\f197"}* .fa-speakap:before{content:"\f3f3"}* .fa-spider:before{content:"\f717"}* .fa-spinner:before{content:"\f110"}* .fa-splotch:before{content:"\f5bc"}* .fa-spotify:before{content:"\f1bc"}* .fa-spray-can:before{content:"\f5bd"}* .fa-square:before{content:"\f0c8"}* .fa-square-full:before{content:"\f45c"}* .fa-square-root-alt:before{content:"\f698"}* .fa-squarespace:before{content:"\f5be"}* .fa-stack-exchange:before{content:"\f18d"}* .fa-stack-overflow:before{content:"\f16c"}* .fa-stamp:before{content:"\f5bf"}* .fa-star:before{content:"\f005"}* .fa-star-and-crescent:before{content:"\f699"}* .fa-star-half:before{content:"\f089"}* .fa-star-half-alt:before{content:"\f5c0"}* .fa-star-of-david:before{content:"\f69a"}* .fa-star-of-life:before{content:"\f621"}* .fa-staylinked:before{content:"\f3f5"}* .fa-steam:before{content:"\f1b6"}* .fa-steam-square:before{content:"\f1b7"}* .fa-steam-symbol:before{content:"\f3f6"}* .fa-step-backward:before{content:"\f048"}* .fa-step-forward:before{content:"\f051"}* .fa-stethoscope:before{content:"\f0f1"}* .fa-sticker-mule:before{content:"\f3f7"}* .fa-sticky-note:before{content:"\f249"}* .fa-stop:before{content:"\f04d"}* .fa-stop-circle:before{content:"\f28d"}* .fa-stopwatch:before{content:"\f2f2"}* .fa-store:before{content:"\f54e"}* .fa-store-alt:before{content:"\f54f"}* .fa-strava:before{content:"\f428"}* .fa-stream:before{content:"\f550"}* .fa-street-view:before{content:"\f21d"}* .fa-strikethrough:before{content:"\f0cc"}* .fa-stripe:before{content:"\f429"}* .fa-stripe-s:before{content:"\f42a"}* .fa-stroopwafel:before{content:"\f551"}* .fa-studiovinari:before{content:"\f3f8"}* .fa-stumbleupon:before{content:"\f1a4"}* .fa-stumbleupon-circle:before{content:"\f1a3"}* .fa-subscript:before{content:"\f12c"}* .fa-subway:before{content:"\f239"}* .fa-suitcase:before{content:"\f0f2"}* .fa-suitcase-rolling:before{content:"\f5c1"}* .fa-sun:before{content:"\f185"}* .fa-superpowers:before{content:"\f2dd"}* .fa-superscript:before{content:"\f12b"}* .fa-supple:before{content:"\f3f9"}* .fa-surprise:before{content:"\f5c2"}* .fa-suse:before{content:"\f7d6"}* .fa-swatchbook:before{content:"\f5c3"}* .fa-swimmer:before{content:"\f5c4"}* .fa-swimming-pool:before{content:"\f5c5"}* .fa-synagogue:before{content:"\f69b"}* .fa-sync:before{content:"\f021"}* .fa-sync-alt:before{content:"\f2f1"}* .fa-syringe:before{content:"\f48e"}* .fa-table:before{content:"\f0ce"}* .fa-table-tennis:before{content:"\f45d"}* .fa-tablet:before{content:"\f10a"}* .fa-tablet-alt:before{content:"\f3fa"}* .fa-tablets:before{content:"\f490"}* .fa-tachometer-alt:before{content:"\f3fd"}* .fa-tag:before{content:"\f02b"}* .fa-tags:before{content:"\f02c"}* .fa-tape:before{content:"\f4db"}* .fa-tasks:before{content:"\f0ae"}* .fa-taxi:before{content:"\f1ba"}* .fa-teamspeak:before{content:"\f4f9"}* .fa-teeth:before{content:"\f62e"}* .fa-teeth-open:before{content:"\f62f"}* .fa-telegram:before{content:"\f2c6"}* .fa-telegram-plane:before{content:"\f3fe"}* .fa-temperature-high:before{content:"\f769"}* .fa-temperature-low:before{content:"\f76b"}* .fa-tencent-weibo:before{content:"\f1d5"}* .fa-tenge:before{content:"\f7d7"}* .fa-terminal:before{content:"\f120"}* .fa-text-height:before{content:"\f034"}* .fa-text-width:before{content:"\f035"}* .fa-th:before{content:"\f00a"}* .fa-th-large:before{content:"\f009"}* .fa-th-list:before{content:"\f00b"}* .fa-the-red-yeti:before{content:"\f69d"}* .fa-theater-masks:before{content:"\f630"}* .fa-themeco:before{content:"\f5c6"}* .fa-themeisle:before{content:"\f2b2"}* .fa-thermometer:before{content:"\f491"}* .fa-thermometer-empty:before{content:"\f2cb"}* .fa-thermometer-full:before{content:"\f2c7"}* .fa-thermometer-half:before{content:"\f2c9"}* .fa-thermometer-quarter:before{content:"\f2ca"}* .fa-thermometer-three-quarters:before{content:"\f2c8"}* .fa-think-peaks:before{content:"\f731"}* .fa-thumbs-down:before{content:"\f165"}* .fa-thumbs-up:before{content:"\f164"}* .fa-thumbtack:before{content:"\f08d"}* .fa-ticket-alt:before{content:"\f3ff"}* .fa-times:before{content:"\f00d"}* .fa-times-circle:before{content:"\f057"}* .fa-tint:before{content:"\f043"}* .fa-tint-slash:before{content:"\f5c7"}* .fa-tired:before{content:"\f5c8"}* .fa-toggle-off:before{content:"\f204"}* .fa-toggle-on:before{content:"\f205"}* .fa-toilet:before{content:"\f7d8"}* .fa-toilet-paper:before{content:"\f71e"}* .fa-toolbox:before{content:"\f552"}* .fa-tools:before{content:"\f7d9"}* .fa-tooth:before{content:"\f5c9"}* .fa-torah:before{content:"\f6a0"}* .fa-torii-gate:before{content:"\f6a1"}* .fa-tractor:before{content:"\f722"}* .fa-trade-federation:before{content:"\f513"}* .fa-trademark:before{content:"\f25c"}* .fa-traffic-light:before{content:"\f637"}* .fa-train:before{content:"\f238"}* .fa-tram:before{content:"\f7da"}* .fa-transgender:before{content:"\f224"}* .fa-transgender-alt:before{content:"\f225"}* .fa-trash:before{content:"\f1f8"}* .fa-trash-alt:before{content:"\f2ed"}* .fa-tree:before{content:"\f1bb"}* .fa-trello:before{content:"\f181"}* .fa-tripadvisor:before{content:"\f262"}* .fa-trophy:before{content:"\f091"}* .fa-truck:before{content:"\f0d1"}* .fa-truck-loading:before{content:"\f4de"}* .fa-truck-monster:before{content:"\f63b"}* .fa-truck-moving:before{content:"\f4df"}* .fa-truck-pickup:before{content:"\f63c"}* .fa-tshirt:before{content:"\f553"}* .fa-tty:before{content:"\f1e4"}* .fa-tumblr:before{content:"\f173"}* .fa-tumblr-square:before{content:"\f174"}* .fa-tv:before{content:"\f26c"}* .fa-twitch:before{content:"\f1e8"}* .fa-twitter:before{content:"\f099"}* .fa-twitter-square:before{content:"\f081"}* .fa-typo3:before{content:"\f42b"}* .fa-uber:before{content:"\f402"}* .fa-ubuntu:before{content:"\f7df"}* .fa-uikit:before{content:"\f403"}* .fa-umbrella:before{content:"\f0e9"}* .fa-umbrella-beach:before{content:"\f5ca"}* .fa-underline:before{content:"\f0cd"}* .fa-undo:before{content:"\f0e2"}* .fa-undo-alt:before{content:"\f2ea"}* .fa-uniregistry:before{content:"\f404"}* .fa-universal-access:before{content:"\f29a"}* .fa-university:before{content:"\f19c"}* .fa-unlink:before{content:"\f127"}* .fa-unlock:before{content:"\f09c"}* .fa-unlock-alt:before{content:"\f13e"}* .fa-untappd:before{content:"\f405"}* .fa-upload:before{content:"\f093"}* .fa-ups:before{content:"\f7e0"}* .fa-usb:before{content:"\f287"}* .fa-user:before{content:"\f007"}* .fa-user-alt:before{content:"\f406"}* .fa-user-alt-slash:before{content:"\f4fa"}* .fa-user-astronaut:before{content:"\f4fb"}* .fa-user-check:before{content:"\f4fc"}* .fa-user-circle:before{content:"\f2bd"}* .fa-user-clock:before{content:"\f4fd"}* .fa-user-cog:before{content:"\f4fe"}* .fa-user-edit:before{content:"\f4ff"}* .fa-user-friends:before{content:"\f500"}* .fa-user-graduate:before{content:"\f501"}* .fa-user-injured:before{content:"\f728"}* .fa-user-lock:before{content:"\f502"}* .fa-user-md:before{content:"\f0f0"}* .fa-user-minus:before{content:"\f503"}* .fa-user-ninja:before{content:"\f504"}* .fa-user-plus:before{content:"\f234"}* .fa-user-secret:before{content:"\f21b"}* .fa-user-shield:before{content:"\f505"}* .fa-user-slash:before{content:"\f506"}* .fa-user-tag:before{content:"\f507"}* .fa-user-tie:before{content:"\f508"}* .fa-user-times:before{content:"\f235"}* .fa-users:before{content:"\f0c0"}* .fa-users-cog:before{content:"\f509"}* .fa-usps:before{content:"\f7e1"}* .fa-ussunnah:before{content:"\f407"}* .fa-utensil-spoon:before{content:"\f2e5"}* .fa-utensils:before{content:"\f2e7"}* .fa-vaadin:before{content:"\f408"}* .fa-vector-square:before{content:"\f5cb"}* .fa-venus:before{content:"\f221"}* .fa-venus-double:before{content:"\f226"}* .fa-venus-mars:before{content:"\f228"}* .fa-viacoin:before{content:"\f237"}* .fa-viadeo:before{content:"\f2a9"}* .fa-viadeo-square:before{content:"\f2aa"}* .fa-vial:before{content:"\f492"}* .fa-vials:before{content:"\f493"}* .fa-viber:before{content:"\f409"}* .fa-video:before{content:"\f03d"}* .fa-video-slash:before{content:"\f4e2"}* .fa-vihara:before{content:"\f6a7"}* .fa-vimeo:before{content:"\f40a"}* .fa-vimeo-square:before{content:"\f194"}* .fa-vimeo-v:before{content:"\f27d"}* .fa-vine:before{content:"\f1ca"}* .fa-vk:before{content:"\f189"}* .fa-vnv:before{content:"\f40b"}* .fa-volleyball-ball:before{content:"\f45f"}* .fa-volume-down:before{content:"\f027"}* .fa-volume-mute:before{content:"\f6a9"}* .fa-volume-off:before{content:"\f026"}* .fa-volume-up:before{content:"\f028"}* .fa-vote-yea:before{content:"\f772"}* .fa-vr-cardboard:before{content:"\f729"}* .fa-vuejs:before{content:"\f41f"}* .fa-walking:before{content:"\f554"}* .fa-wallet:before{content:"\f555"}* .fa-warehouse:before{content:"\f494"}* .fa-water:before{content:"\f773"}* .fa-weebly:before{content:"\f5cc"}* .fa-weibo:before{content:"\f18a"}* .fa-weight:before{content:"\f496"}* .fa-weight-hanging:before{content:"\f5cd"}* .fa-weixin:before{content:"\f1d7"}* .fa-whatsapp:before{content:"\f232"}* .fa-whatsapp-square:before{content:"\f40c"}* .fa-wheelchair:before{content:"\f193"}* .fa-whmcs:before{content:"\f40d"}* .fa-wifi:before{content:"\f1eb"}* .fa-wikipedia-w:before{content:"\f266"}* .fa-wind:before{content:"\f72e"}* .fa-window-close:before{content:"\f410"}* .fa-window-maximize:before{content:"\f2d0"}* .fa-window-minimize:before{content:"\f2d1"}* .fa-window-restore:before{content:"\f2d2"}* .fa-windows:before{content:"\f17a"}* .fa-wine-bottle:before{content:"\f72f"}* .fa-wine-glass:before{content:"\f4e3"}* .fa-wine-glass-alt:before{content:"\f5ce"}* .fa-wix:before{content:"\f5cf"}* .fa-wizards-of-the-coast:before{content:"\f730"}* .fa-wolf-pack-battalion:before{content:"\f514"}* .fa-won-sign:before{content:"\f159"}* .fa-wordpress:before{content:"\f19a"}* .fa-wordpress-simple:before{content:"\f411"}* .fa-wpbeginner:before{content:"\f297"}* .fa-wpexplorer:before{content:"\f2de"}* .fa-wpforms:before{content:"\f298"}* .fa-wpressr:before{content:"\f3e4"}* .fa-wrench:before{content:"\f0ad"}* .fa-x-ray:before{content:"\f497"}* .fa-xbox:before{content:"\f412"}* .fa-xing:before{content:"\f168"}* .fa-xing-square:before{content:"\f169"}* .fa-y-combinator:before{content:"\f23b"}* .fa-yahoo:before{content:"\f19e"}* .fa-yandex:before{content:"\f413"}* .fa-yandex-international:before{content:"\f414"}* .fa-yarn:before{content:"\f7e3"}* .fa-yelp:before{content:"\f1e9"}* .fa-yen-sign:before{content:"\f157"}* .fa-yin-yang:before{content:"\f6ad"}* .fa-yoast:before{content:"\f2b1"}* .fa-youtube:before{content:"\f167"}* .fa-youtube-square:before{content:"\f431"}* .fa-zhihu:before{content:"\f63f"}* .sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}* .sr-only-focusable:active,* .sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:900;src:url(assets/fonts/webfonts/fa-solid-900.eot);src:url(assets/fonts/webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(assets/fonts/webfonts/fa-solid-900.woff2) format("woff2"),url(assets/fonts/webfonts/fa-solid-900.woff) format("woff"),url(assets/fonts/webfonts/fa-solid-900.ttf) format("truetype"),url(assets/fonts/webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:Font Awesome\ 5 Free;font-weight:900}@font-face{font-family:pficon;src:url(assets/pficon/pficon.eot);src:url(assets/pficon/pficon.eot?#iefix) format("eot"),url(assets/pficon/pficon.woff2) format("woff2"),url(assets/pficon/pficon.woff) format("woff"),url(assets/pficon/pficon.ttf) format("truetype"),url(assets/pficon/pficon.svg#pficon) format("svg")}.pf-icon-aa-circle-o:before,.pf-icon-add-circle-o:before,.pf-icon-ansible-tower:before,.pf-icon-applications:before,.pf-icon-arrow:before,.pf-icon-asleep:before,.pf-icon-attention-bell:before,.pf-icon-automation:before,.pf-icon-bell:before,.pf-icon-blueprint:before,.pf-icon-build:before,.pf-icon-builder-image:before,.pf-icon-bundle:before,.pf-icon-catalog:before,.pf-icon-chat:before,.pf-icon-close:before,.pf-icon-cloud-security:before,.pf-icon-cloud-tenant:before,.pf-icon-cluster:before,.pf-icon-connected:before,.pf-icon-container-node:before,.pf-icon-cpu:before,.pf-icon-degraded:before,.pf-icon-disconnected:before,.pf-icon-domain:before,.pf-icon-edit:before,.pf-icon-enhancement:before,.pf-icon-enterprise:before,.pf-icon-equalizer:before,.pf-icon-error-circle-o:before,.pf-icon-export:before,.pf-icon-filter:before,.pf-icon-flavor:before,.pf-icon-folder-close:before,.pf-icon-folder-open:before,.pf-icon-globe-route:before,.pf-icon-help:before,.pf-icon-history:before,.pf-icon-home:before,.pf-icon-import:before,.pf-icon-in-progress:before,.pf-icon-info:before,.pf-icon-infrastructure:before,.pf-icon-integration:before,.pf-icon-key:before,.pf-icon-locked:before,.pf-icon-maintenance:before,.pf-icon-memory:before,.pf-icon-messages:before,.pf-icon-middleware:before,.pf-icon-migration:before,.pf-icon-module:before,.pf-icon-monitoring:before,.pf-icon-namespaces:before,.pf-icon-network:before,.pf-icon-new-process:before,.pf-icon-not-started:before,.pf-icon-off:before,.pf-icon-ok:before,.pf-icon-on-running:before,.pf-icon-on:before,.pf-icon-openshift:before,.pf-icon-openstack:before,.pf-icon-optimize:before,.pf-icon-orders:before,.pf-icon-os-image:before,.pf-icon-package:before,.pf-icon-paused:before,.pf-icon-pending:before,.pf-icon-pficon-dragdrop:before,.pf-icon-pficon-history:before,.pf-icon-pficon-network-range:before,.pf-icon-pficon-satellite:before,.pf-icon-pficon-sort-common-asc:before,.pf-icon-pficon-sort-common-desc:before,.pf-icon-pficon-template:before,.pf-icon-pficon-vcenter:before,.pf-icon-plugged:before,.pf-icon-port:before,.pf-icon-print:before,.pf-icon-private:before,.pf-icon-process-automation:before,.pf-icon-project:before,.pf-icon-rebalance:before,.pf-icon-rebooting:before,.pf-icon-regions:before,.pf-icon-registry:before,.pf-icon-remove2:before,.pf-icon-replicator:before,.pf-icon-repository:before,.pf-icon-resource-pool:before,.pf-icon-resources-almost-empty:before,.pf-icon-resources-almost-full:before,.pf-icon-resources-empty:before,.pf-icon-resources-full:before,.pf-icon-running:before,.pf-icon-save:before,.pf-icon-screen:before,.pf-icon-security:before,.pf-icon-server-group:before,.pf-icon-server:before,.pf-icon-service-catalog:before,.pf-icon-service:before,.pf-icon-services:before,.pf-icon-spinner2:before,.pf-icon-spinner:before,.pf-icon-storage-domain:before,.pf-icon-tenant:before,.pf-icon-thumb-tack:before,.pf-icon-topology:before,.pf-icon-trend-down:before,.pf-icon-trend-up:before,.pf-icon-unknown:before,.pf-icon-unlocked:before,.pf-icon-unplugged:before,.pf-icon-user:before,.pf-icon-users:before,.pf-icon-virtual-machine:before,.pf-icon-volume:before,.pf-icon-warning-triangle:before,.pf-icon-zone:before{font-family:pficon;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-style:normal;font-variant:normal;font-weight:400;text-decoration:none;text-transform:none}.pf-icon-aa-circle-o:before{content:""}.pf-icon-add-circle-o:before{content:""}.pf-icon-ansible-tower:before{content:""}.pf-icon-applications:before{content:""}.pf-icon-arrow:before{content:""}.pf-icon-asleep:before{content:""}.pf-icon-attention-bell:before{content:""}.pf-icon-automation:before{content:""}.pf-icon-bell:before{content:""}.pf-icon-blueprint:before{content:""}.pf-icon-build:before{content:""}.pf-icon-builder-image:before{content:""}.pf-icon-bundle:before{content:""}.pf-icon-catalog:before{content:""}.pf-icon-chat:before{content:""}.pf-icon-close:before{content:""}.pf-icon-cloud-security:before{content:""}.pf-icon-cloud-tenant:before{content:""}.pf-icon-cluster:before{content:""}.pf-icon-connected:before{content:""}.pf-icon-container-node:before{content:""}.pf-icon-cpu:before{content:""}.pf-icon-degraded:before{content:""}.pf-icon-disconnected:before{content:""}.pf-icon-domain:before{content:""}.pf-icon-edit:before{content:""}.pf-icon-enhancement:before{content:""}.pf-icon-enterprise:before{content:""}.pf-icon-equalizer:before{content:""}.pf-icon-error-circle-o:before{content:""}.pf-icon-export:before{content:""}.pf-icon-filter:before{content:""}.pf-icon-flavor:before{content:""}.pf-icon-folder-close:before{content:""}.pf-icon-folder-open:before{content:""}.pf-icon-globe-route:before{content:""}.pf-icon-help:before{content:""}.pf-icon-history:before{content:""}.pf-icon-home:before{content:""}.pf-icon-import:before{content:""}.pf-icon-in-progress:before{content:""}.pf-icon-info:before{content:""}.pf-icon-infrastructure:before{content:""}.pf-icon-integration:before{content:""}.pf-icon-key:before{content:""}.pf-icon-locked:before{content:""}.pf-icon-maintenance:before{content:""}.pf-icon-memory:before{content:""}.pf-icon-messages:before{content:""}.pf-icon-middleware:before{content:""}.pf-icon-migration:before{content:""}.pf-icon-module:before{content:""}.pf-icon-monitoring:before{content:""}.pf-icon-namespaces:before{content:""}.pf-icon-network:before{content:""}.pf-icon-new-process:before{content:""}.pf-icon-not-started:before{content:""}.pf-icon-off:before{content:""}.pf-icon-ok:before{content:""}.pf-icon-on-running:before{content:""}.pf-icon-on:before{content:""}.pf-icon-openshift:before{content:""}.pf-icon-openstack:before{content:""}.pf-icon-optimize:before{content:""}.pf-icon-orders:before{content:""}.pf-icon-os-image:before{content:""}.pf-icon-package:before{content:""}.pf-icon-paused:before{content:""}.pf-icon-pending:before{content:""}.pf-icon-pficon-dragdrop:before{content:""}.pf-icon-pficon-history:before{content:""}.pf-icon-pficon-network-range:before{content:""}.pf-icon-pficon-satellite:before{content:""}.pf-icon-pficon-sort-common-asc:before{content:""}.pf-icon-pficon-sort-common-desc:before{content:""}.pf-icon-pficon-template:before{content:""}.pf-icon-pficon-vcenter:before{content:""}.pf-icon-plugged:before{content:""}.pf-icon-port:before{content:""}.pf-icon-print:before{content:""}.pf-icon-private:before{content:""}.pf-icon-process-automation:before{content:""}.pf-icon-project:before{content:""}.pf-icon-rebalance:before{content:""}.pf-icon-rebooting:before{content:""}.pf-icon-regions:before{content:""}.pf-icon-registry:before{content:""}.pf-icon-remove2:before{content:""}.pf-icon-replicator:before{content:""}.pf-icon-repository:before{content:""}.pf-icon-resource-pool:before{content:""}.pf-icon-resources-almost-empty:before{content:""}.pf-icon-resources-almost-full:before{content:""}.pf-icon-resources-empty:before{content:""}.pf-icon-resources-full:before{content:""}.pf-icon-running:before{content:""}.pf-icon-save:before{content:""}.pf-icon-screen:before{content:""}.pf-icon-security:before{content:""}.pf-icon-server-group:before{content:""}.pf-icon-server:before{content:""}.pf-icon-service-catalog:before{content:""}.pf-icon-service:before{content:""}.pf-icon-services:before{content:""}.pf-icon-spinner:before{content:""}.pf-icon-spinner2:before{content:""}.pf-icon-storage-domain:before{content:""}.pf-icon-tenant:before{content:""}.pf-icon-thumb-tack:before{content:""}.pf-icon-topology:before{content:""}.pf-icon-trend-down:before{content:""}.pf-icon-trend-up:before{content:""}.pf-icon-unknown:before{content:""}.pf-icon-unlocked:before{content:""}.pf-icon-unplugged:before{content:""}.pf-icon-user:before{content:""}.pf-icon-users:before{content:""}.pf-icon-virtual-machine:before{content:""}.pf-icon-volume:before{content:""}.pf-icon-warning-triangle:before{content:""}.pf-icon-zone:before{content:""}.pf-c-about-modal-box{--pf-c-about-modal-box--BackgroundColor:var(--pf-global--palette--black-1000);--pf-c-about-modal-box--BoxShadow:0 0 100px 0 hsla(0,0%,100%,0.05);--pf-c-about-modal-box--ZIndex:var(--pf-global--ZIndex--xl);--pf-c-about-modal-box--Height:100%;--pf-c-about-modal-box--lg--Height:47.625rem;--pf-c-about-modal-box--lg--MaxHeight:calc(100% - var(--pf-global--spacer--xl));--pf-c-about-modal-box--Width:100vw;--pf-c-about-modal-box--lg--Width:calc(100% - var(--pf-global--spacer--3xl)*2);--pf-c-about-modal-box--lg--MaxWidth:77rem;--pf-c-about-modal-box--PaddingTop:var(--pf-global--spacer--xl);--pf-c-about-modal-box--PaddingRight:var(--pf-global--spacer--xl);--pf-c-about-modal-box--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-about-modal-box--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-about-modal-box--sm--PaddingTop:var(--pf-global--spacer--3xl);--pf-c-about-modal-box--sm--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-about-modal-box--sm--PaddingBottom:var(--pf-global--spacer--3xl);--pf-c-about-modal-box--sm--PaddingLeft:var(--pf-global--spacer--3xl);--pf-c-about-modal-box--sm--grid-template-columns:5fr 1fr;--pf-c-about-modal-box--lg--grid-template-columns:1fr .6fr;--pf-c-about-modal-box__brand--PaddingTop:var(--pf-global--spacer--2xl);--pf-c-about-modal-box__brand--PaddingRight:var(--pf-global--spacer--xl);--pf-c-about-modal-box__brand--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-about-modal-box__brand--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-about-modal-box__brand--sm--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__brand--sm--PaddingLeft:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__brand--sm--PaddingBottom:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__close--ZIndex:var(--pf-global--ZIndex--2xl);--pf-c-about-modal-box__close--PaddingTop:var(--pf-global--spacer--2xl);--pf-c-about-modal-box__close--PaddingRight:var(--pf-global--spacer--xl);--pf-c-about-modal-box__close--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-about-modal-box__close--sm--PaddingBottom:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__close--sm--PaddingRight:0;--pf-c-about-modal-box__close--lg--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__close--c-button--Color:var(--pf-global--Color--100);--pf-c-about-modal-box__close--c-button--FontSize:var(--pf-global--FontSize--xl);--pf-c-about-modal-box__close--c-button--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-about-modal-box__close--c-button--Width:calc(var(--pf-c-about-modal-box__close--c-button--FontSize)*2);--pf-c-about-modal-box__close--c-button--Height:calc(var(--pf-c-about-modal-box__close--c-button--FontSize)*2);--pf-c-about-modal-box__close--c-button--BackgroundColor:var(--pf-global--palette--black-1000);--pf-c-about-modal-box__close--c-button--hover--BackgroundColor:rgba(3,3,3,0.4);--pf-c-about-modal-box__hero--sm--BackgroundImage:url(assets/images/pfbg_992@2x.jpg);--pf-c-about-modal-box__hero--sm--BackgroundPosition:top left;--pf-c-about-modal-box__hero--sm--BackgroundSize:cover;--pf-c-about-modal-box__brand-image--Height:2.5rem;--pf-c-about-modal-box__header--PaddingRight:var(--pf-global--spacer--xl);--pf-c-about-modal-box__header--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-about-modal-box__header--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-about-modal-box__header--sm--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__header--sm--PaddingLeft:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__strapline--PaddingTop:var(--pf-global--spacer--xl);--pf-c-about-modal-box__strapline--FontSize:var(--pf-global--FontSize--sm);--pf-c-about-modal-box__strapline--sm--PaddingTop:var(--pf-global--spacer--2xl);--pf-c-about-modal-box__content--MarginTop:var(--pf-global--spacer--xl);--pf-c-about-modal-box__content--MarginRight:var(--pf-global--spacer--xl);--pf-c-about-modal-box__content--MarginBottom:var(--pf-global--spacer--xl);--pf-c-about-modal-box__content--MarginLeft:var(--pf-global--spacer--xl);--pf-c-about-modal-box__content--sm--MarginTop:var(--pf-global--spacer--2xl);--pf-c-about-modal-box__content--sm--MarginRight:var(--pf-global--spacer--3xl);--pf-c-about-modal-box__content--sm--MarginBottom:var(--pf-global--spacer--2xl);--pf-c-about-modal-box__content--sm--MarginLeft:var(--pf-global--spacer--3xl);color:var(--pf-global--Color--100);position:relative;z-index:var(--pf-c-about-modal-box--ZIndex);display:grid;grid-template-rows:max-content max-content auto;grid-template-areas:"brand close" "header header" "content content";width:var(--pf-c-about-modal-box--Width);height:var(--pf-c-about-modal-box--Height);overflow-x:hidden;overflow-y:auto;background-color:var(--pf-c-about-modal-box--BackgroundColor);box-shadow:var(--pf-c-about-modal-box--BoxShadow)}@media screen and (min-width:576px){.pf-c-about-modal-box{--pf-c-about-modal-box--PaddingTop:var(--pf-c-about-modal-box--sm--PaddingTop);--pf-c-about-modal-box--PaddingRight:var(--pf-c-about-modal-box--sm--PaddingRight);--pf-c-about-modal-box--PaddingBottom:var(--pf-c-about-modal-box--sm--PaddingBottom);--pf-c-about-modal-box--PaddingLeft:var(--pf-c-about-modal-box--sm--PaddingLeft);--pf-c-about-modal-box__brand--PaddingRight:var(--pf-c-about-modal-box__brand--sm--PaddingRight);--pf-c-about-modal-box__brand--PaddingLeft:var(--pf-c-about-modal-box__brand--sm--PaddingLeft);--pf-c-about-modal-box__brand--PaddingBottom:var(--pf-c-about-modal-box__brand--sm--PaddingBottom)}}@media only screen and (min-width:576px){.pf-c-about-modal-box{--pf-c-about-modal-box__close--PaddingRight:var(--pf-c-about-modal-box__close--sm--PaddingRight);--pf-c-about-modal-box__close--PaddingBottom:var(--pf-c-about-modal-box__close--sm--PaddingBottom)}}@media only screen and (min-width:992px){.pf-c-about-modal-box{--pf-c-about-modal-box__close--PaddingRight:var(--pf-c-about-modal-box__close--lg--PaddingRight)}}@media only screen and (min-width:576px){.pf-c-about-modal-box{--pf-c-about-modal-box__header--PaddingRight:var(--pf-c-about-modal-box__header--sm--PaddingRight);--pf-c-about-modal-box__header--PaddingLeft:var(--pf-c-about-modal-box__header--sm--PaddingLeft);--pf-c-about-modal-box__strapline--PaddingTop:var(--pf-c-about-modal-box__strapline--sm--PaddingTop);--pf-c-about-modal-box__content--MarginTop:var(--pf-c-about-modal-box__content--sm--MarginTop);--pf-c-about-modal-box__content--MarginRight:var(--pf-c-about-modal-box__content--sm--MarginRight);--pf-c-about-modal-box__content--MarginBottom:var(--pf-c-about-modal-box__content--sm--MarginBottom);--pf-c-about-modal-box__content--MarginLeft:var(--pf-c-about-modal-box__content--sm--MarginLeft);grid-template-columns:var(--pf-c-about-modal-box--sm--grid-template-columns);grid-template-areas:"brand hero" "header hero" "content hero"}}@media only screen and (min-width:992px){.pf-c-about-modal-box{--pf-c-about-modal-box--Height:var(--pf-c-about-modal-box--lg--Height);--pf-c-about-modal-box--Width:var(--pf-c-about-modal-box--lg--Width);grid-template-columns:var(--pf-c-about-modal-box--lg--grid-template-columns);grid-template-rows:max-content max-content auto;max-width:var(--pf-c-about-modal-box--lg--MaxWidth);max-height:var(--pf-c-about-modal-box--lg--MaxHeight)}}.pf-c-about-modal-box__brand{grid-area:brand;display:flex;padding:var(--pf-c-about-modal-box__brand--PaddingTop) var(--pf-c-about-modal-box__brand--PaddingRight) var(--pf-c-about-modal-box__brand--PaddingBottom) var(--pf-c-about-modal-box__brand--PaddingLeft)}.pf-c-about-modal-box__brand-image{height:var(--pf-c-about-modal-box__brand-image--Height)}.pf-c-about-modal-box__header{grid-area:header;display:flex;flex-direction:column;padding-right:var(--pf-c-about-modal-box__header--PaddingRight);padding-bottom:var(--pf-c-about-modal-box__header--PaddingBottom);padding-left:var(--pf-c-about-modal-box__header--PaddingLeft)}.pf-c-about-modal-box__strapline{padding-top:var(--pf-c-about-modal-box__strapline--PaddingTop);margin-top:auto;font-size:var(--pf-c-about-modal-box__strapline--FontSize)}.pf-c-about-modal-box__content{display:flex;flex-direction:column;grid-area:content;margin:var(--pf-c-about-modal-box__content--MarginTop) var(--pf-c-about-modal-box__content--MarginRight) var(--pf-c-about-modal-box__content--MarginBottom) var(--pf-c-about-modal-box__content--MarginLeft);overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;word-break:break-word}@media screen and (min-width:576px){.pf-c-about-modal-box__content{overflow:visible;overscroll-behavior:auto}}.pf-c-about-modal-box__close{grid-area:close;position:sticky;top:0;display:flex;align-items:flex-start;justify-content:flex-end;padding-top:var(--pf-c-about-modal-box__close--PaddingTop);padding-right:var(--pf-c-about-modal-box__close--PaddingRight);padding-bottom:var(--pf-c-about-modal-box__close--PaddingBottom)}@media only screen and (min-width:576px){.pf-c-about-modal-box__close{grid-area:1/2;justify-content:center}}@media only screen and (min-width:992px){.pf-c-about-modal-box__close{justify-content:flex-end}}.pf-c-about-modal-box__close .pf-c-button.pf-m-plain{display:flex;align-items:center;justify-content:center;width:var(--pf-c-about-modal-box__close--c-button--Width);height:var(--pf-c-about-modal-box__close--c-button--Height);font-size:var(--pf-c-about-modal-box__close--c-button--FontSize);color:var(--pf-c-about-modal-box__close--c-button--Color);background-color:var(--pf-c-about-modal-box__close--c-button--BackgroundColor);border-radius:var(--pf-c-about-modal-box__close--c-button--BorderRadius)}.pf-c-about-modal-box__close .pf-c-button.pf-m-plain:hover{--pf-c-about-modal-box__close--c-button--BackgroundColor:var(--pf-c-about-modal-box__close--c-button--hover--BackgroundColor)}.pf-c-about-modal-box__hero{display:none;visibility:hidden}@media only screen and (min-width:576px){.pf-c-about-modal-box__hero{display:block;visibility:visible;background-image:var(--pf-c-about-modal-box__hero--sm--BackgroundImage);background-repeat:no-repeat;background-attachment:fixed;background-position:var(--pf-c-about-modal-box__hero--sm--BackgroundPosition);background-size:var(--pf-c-about-modal-box__hero--sm--BackgroundSize);grid-area:hero}}.pf-c-accordion{--pf-c-accordion--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-accordion__toggle--PaddingTop:var(--pf-global--spacer--sm);--pf-c-accordion__toggle--PaddingRight:var(--pf-global--spacer--md);--pf-c-accordion__toggle--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-accordion__toggle--PaddingLeft:var(--pf-global--spacer--md);--pf-c-accordion__toggle--before--BackgroundColor:transparent;--pf-c-accordion__toggle--hover--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-accordion__toggle--focus--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-accordion__toggle--active--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-accordion__toggle--before--Width:var(--pf-global--BorderWidth--lg);--pf-c-accordion__toggle--m-expanded--before--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-accordion__toggle-text--MaxWidth:calc(100% - var(--pf-global--spacer--lg));--pf-c-accordion__toggle--hover__toggle-text--Color:var(--pf-global--link--Color);--pf-c-accordion__toggle--active__toggle-text--Color:var(--pf-global--link--Color);--pf-c-accordion__toggle--active__toggle-text--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-accordion__toggle--focus__toggle-text--Color:var(--pf-global--link--Color);--pf-c-accordion__toggle--focus__toggle-text--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-accordion__toggle--m-expanded__toggle-text--Color:var(--pf-global--link--Color);--pf-c-accordion__toggle--m-expanded__toggle-text--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-accordion__toggle-icon--Transition:.2s ease-in 0s;--pf-c-accordion__toggle--m-expanded__toggle-icon--Rotate:90deg;--pf-c-accordion__expanded-content-body--PaddingTop:var(--pf-global--spacer--sm);--pf-c-accordion__expanded-content-body--PaddingRight:var(--pf-global--spacer--md);--pf-c-accordion__expanded-content-body--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-accordion__expanded-content-body--PaddingLeft:var(--pf-global--spacer--md);--pf-c-accordion__expanded-content--Color:var(--pf-global--Color--200);--pf-c-accordion__expanded-content--FontSize:var(--pf-global--FontSize--sm);--pf-c-accordion__expanded-content-body--before--BackgroundColor:transparent;--pf-c-accordion__expanded-content-body--before--Width:var(--pf-global--BorderWidth--lg);--pf-c-accordion__expanded-content--m-expanded__expanded-content-body--before--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-accordion__expanded-content--m-fixed--MaxHeight:9.375rem;color:var(--pf-global--Color--100);background-color:var(--pf-c-accordion--BackgroundColor)}.pf-c-accordion__toggle{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;padding:var(--pf-c-accordion__toggle--PaddingTop) var(--pf-c-accordion__toggle--PaddingRight) var(--pf-c-accordion__toggle--PaddingBottom) var(--pf-c-accordion__toggle--PaddingLeft);border:0}.pf-c-accordion__toggle:before{position:absolute;top:0;bottom:0;left:0;width:var(--pf-c-accordion__toggle--before--Width);content:"";background-color:var(--pf-c-accordion__toggle--before--BackgroundColor)}.pf-c-accordion__toggle.pf-m-expanded{--pf-c-accordion__toggle--before--BackgroundColor:var(--pf-c-accordion__toggle--m-expanded--before--BackgroundColor)}.pf-c-accordion__toggle.pf-m-expanded .pf-c-accordion__toggle-text{font-weight:var(--pf-c-accordion__toggle--m-expanded__toggle-text--FontWeight);color:var(--pf-c-accordion__toggle--m-expanded__toggle-text--Color)}.pf-c-accordion__toggle.pf-m-expanded .pf-c-accordion__toggle-icon{transform:rotate(var(--pf-c-accordion__toggle--m-expanded__toggle-icon--Rotate))}.pf-c-accordion__toggle:hover{background-color:var(--pf-c-accordion__toggle--hover--BackgroundColor)}.pf-c-accordion__toggle:hover .pf-c-accordion__toggle-text{color:var(--pf-c-accordion__toggle--hover__toggle-text--Color)}.pf-c-accordion__toggle:focus{background-color:var(--pf-c-accordion__toggle--focus--BackgroundColor)}.pf-c-accordion__toggle:focus .pf-c-accordion__toggle-text{font-weight:var(--pf-c-accordion__toggle--focus__toggle-text--FontWeight);color:var(--pf-c-accordion__toggle--focus__toggle-text--Color)}.pf-c-accordion__toggle:active{background-color:var(--pf-c-accordion__toggle--active--BackgroundColor)}.pf-c-accordion__toggle:active .pf-c-accordion__toggle-text{font-weight:var(--pf-c-accordion__toggle--active__toggle-text--FontWeight);color:var(--pf-c-accordion__toggle--active__toggle-text--Color)}.pf-c-accordion__toggle-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:var(--pf-c-accordion__toggle-text--MaxWidth)}.pf-c-accordion__toggle-icon{transition:var(--pf-c-accordion__toggle-icon--Transition)}.pf-c-accordion__expanded-content{font-size:var(--pf-c-accordion__expanded-content--FontSize);color:var(--pf-c-accordion__expanded-content--Color)}.pf-c-accordion__expanded-content.pf-m-fixed{max-height:var(--pf-c-accordion__expanded-content--m-fixed--MaxHeight);overflow-y:auto}.pf-c-accordion__expanded-content.pf-m-expanded{--pf-c-accordion__expanded-content-body--before--BackgroundColor:var(--pf-c-accordion__expanded-content--m-expanded__expanded-content-body--before--BackgroundColor)}.pf-c-accordion__expanded-content-body{position:relative;padding:var(--pf-c-accordion__expanded-content-body--PaddingTop) var(--pf-c-accordion__expanded-content-body--PaddingRight) var(--pf-c-accordion__expanded-content-body--PaddingBottom) var(--pf-c-accordion__expanded-content-body--PaddingLeft)}.pf-c-accordion__expanded-content-body:before{position:absolute;top:0;bottom:0;left:0;width:var(--pf-c-accordion__expanded-content-body--before--Width);content:"";background-color:var(--pf-c-accordion__expanded-content-body--before--BackgroundColor)}.pf-c-action-list{--pf-c-action-list--m-icon--spacer:0;--pf-c-action-list--child--spacer-base:var(--pf-global--spacer--md);--pf-c-action-list--group--spacer-base:var(--pf-global--spacer--2xl)}.pf-c-action-list,.pf-c-action-list__group{--pf-c-action-list--child--spacer:var(--pf-c-action-list--child--spacer-base);--pf-c-action-list--group--spacer:var(--pf-c-action-list--group--spacer-base);display:flex;align-items:center}.pf-c-action-list>*+*,.pf-c-action-list__group>*+*{margin-left:var(--pf-c-action-list--child--spacer)}.pf-c-action-list .pf-c-action-list__group+*,.pf-c-action-list>*+.pf-c-action-list__group,.pf-c-action-list__group .pf-c-action-list__group+*,.pf-c-action-list__group>*+.pf-c-action-list__group{margin-left:var(--pf-c-action-list--group--spacer)}.pf-c-action-list.pf-m-icons,.pf-c-action-list__group.pf-m-icons{--pf-c-action-list--child--spacer:var(--pf-c-action-list--m-icon--spacer)}.pf-c-alert{--pf-c-alert--BoxShadow:var(--pf-global--BoxShadow--lg);--pf-c-alert--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-alert--GridTemplateColumns:max-content 1fr max-content;--pf-c-alert--BorderTopWidth:var(--pf-global--BorderWidth--md);--pf-c-alert--BorderTopColor:var(--pf-global--default-color--200);--pf-c-alert--PaddingTop:var(--pf-global--spacer--md);--pf-c-alert--PaddingRight:var(--pf-global--spacer--md);--pf-c-alert--PaddingBottom:var(--pf-global--spacer--md);--pf-c-alert--PaddingLeft:var(--pf-global--spacer--md);--pf-c-alert__FontSize:var(--pf-global--FontSize--sm);--pf-c-alert__icon--Color:var(--pf-global--default-color--200);--pf-c-alert__icon--MarginTop:0.0625rem;--pf-c-alert__icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-alert__icon--FontSize:var(--pf-global--icon--FontSize--md);--pf-c-alert__title--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-alert__title--Color:var(--pf-global--default-color--300);--pf-c-alert__title--max-lines:1;--pf-c-alert__action--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-alert__action--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-alert__action--TranslateY:0.125rem;--pf-c-alert__action--MarginRight:calc(var(--pf-global--spacer--sm)*-1);--pf-c-alert__description--PaddingTop:var(--pf-global--spacer--xs);--pf-c-alert__action-group--PaddingTop:var(--pf-global--spacer--xs);--pf-c-alert__description--action-group--PaddingTop:var(--pf-global--spacer--md);--pf-c-alert__action-group__c-button--not-last-child--MarginRight:var(--pf-global--spacer--lg);--pf-c-alert--m-success--BorderTopColor:var(--pf-global--success-color--100);--pf-c-alert--m-success__icon--Color:var(--pf-global--success-color--100);--pf-c-alert--m-success__title--Color:var(--pf-global--success-color--200);--pf-c-alert--m-danger--BorderTopColor:var(--pf-global--danger-color--100);--pf-c-alert--m-danger__icon--Color:var(--pf-global--danger-color--100);--pf-c-alert--m-danger__title--Color:var(--pf-global--danger-color--200);--pf-c-alert--m-warning--BorderTopColor:var(--pf-global--warning-color--100);--pf-c-alert--m-warning__icon--Color:var(--pf-global--warning-color--100);--pf-c-alert--m-warning__title--Color:var(--pf-global--warning-color--200);--pf-c-alert--m-info--BorderTopColor:var(--pf-global--info-color--100);--pf-c-alert--m-info__icon--Color:var(--pf-global--info-color--100);--pf-c-alert--m-info__title--Color:var(--pf-global--info-color--200);--pf-c-alert--m-inline--BoxShadow:none;--pf-c-alert--m-inline--BackgroundColor:var(--pf-global--palette--cyan-50);--pf-c-alert--m-inline--m-success--BackgroundColor:var(--pf-global--palette--green-50);--pf-c-alert--m-inline--m-danger--BackgroundColor:var(--pf-global--palette--red-50);--pf-c-alert--m-inline--m-warning--BackgroundColor:var(--pf-global--palette--gold-50);--pf-c-alert--m-inline--m-info--BackgroundColor:var(--pf-global--palette--blue-50);color:var(--pf-global--Color--100);position:relative;display:grid;padding:var(--pf-c-alert--PaddingTop) var(--pf-c-alert--PaddingRight) var(--pf-c-alert--PaddingBottom) var(--pf-c-alert--PaddingLeft);font-size:var(--pf-c-alert__FontSize);background-color:var(--pf-c-alert--BackgroundColor);border-top:var(--pf-c-alert--BorderTopWidth) solid var(--pf-c-alert--BorderTopColor);box-shadow:var(--pf-c-alert--BoxShadow);grid-template-columns:var(--pf-c-alert--GridTemplateColumns);grid-template-areas:"icon title action" ". description description" ". actiongroup actiongroup"}.pf-c-alert.pf-m-success{--pf-c-alert--BorderTopColor:var(--pf-c-alert--m-success--BorderTopColor);--pf-c-alert__icon--Color:var(--pf-c-alert--m-success__icon--Color);--pf-c-alert__title--Color:var(--pf-c-alert--m-success__title--Color);--pf-c-alert--m-inline--BackgroundColor:var(--pf-c-alert--m-inline--m-success--BackgroundColor)}.pf-c-alert.pf-m-danger{--pf-c-alert--BorderTopColor:var(--pf-c-alert--m-danger--BorderTopColor);--pf-c-alert__icon--Color:var(--pf-c-alert--m-danger__icon--Color);--pf-c-alert__title--Color:var(--pf-c-alert--m-danger__title--Color);--pf-c-alert--m-inline--BackgroundColor:var(--pf-c-alert--m-inline--m-danger--BackgroundColor)}.pf-c-alert.pf-m-warning{--pf-c-alert--BorderTopColor:var(--pf-c-alert--m-warning--BorderTopColor);--pf-c-alert__icon--Color:var(--pf-c-alert--m-warning__icon--Color);--pf-c-alert__title--Color:var(--pf-c-alert--m-warning__title--Color);--pf-c-alert--m-inline--BackgroundColor:var(--pf-c-alert--m-inline--m-warning--BackgroundColor)}.pf-c-alert.pf-m-info{--pf-c-alert--BorderTopColor:var(--pf-c-alert--m-info--BorderTopColor);--pf-c-alert__icon--Color:var(--pf-c-alert--m-info__icon--Color);--pf-c-alert__title--Color:var(--pf-c-alert--m-info__title--Color);--pf-c-alert--m-inline--BackgroundColor:var(--pf-c-alert--m-inline--m-info--BackgroundColor)}.pf-c-alert.pf-m-inline{--pf-c-alert--BoxShadow:var(--pf-c-alert--m-inline--BoxShadow);--pf-c-alert--BackgroundColor:var(--pf-c-alert--m-inline--BackgroundColor)}.pf-c-alert__icon{grid-area:icon;display:flex;margin-top:var(--pf-c-alert__icon--MarginTop);margin-right:var(--pf-c-alert__icon--MarginRight);font-size:var(--pf-c-alert__icon--FontSize);color:var(--pf-c-alert__icon--Color)}.pf-c-alert__title{grid-area:title;font-weight:var(--pf-c-alert__title--FontWeight);color:var(--pf-c-alert__title--Color);word-break:break-word}.pf-c-alert__title.pf-m-truncate{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--pf-c-alert__title--max-lines);overflow:hidden}.pf-c-alert__description{grid-area:description;padding-top:var(--pf-c-alert__description--PaddingTop);word-break:break-word}.pf-c-alert__description+.pf-c-alert__action-group{--pf-c-alert__action-group--PaddingTop:var(--pf-c-alert__description--action-group--PaddingTop)}.pf-c-alert__action{grid-area:action;margin-top:var(--pf-c-alert__action--MarginTop);margin-right:var(--pf-c-alert__action--MarginRight);margin-bottom:var(--pf-c-alert__action--MarginBottom);transform:translateY(var(--pf-c-alert__action--TranslateY))}.pf-c-alert__action>.pf-c-button{--pf-c-button--LineHeight:1}.pf-c-alert__action-group{grid-area:actiongroup;padding-top:var(--pf-c-alert__action-group--PaddingTop)}.pf-c-alert__action-group>.pf-c-button{--pf-c-button--m-link--m-inline--hover--TextDecoration:none}.pf-c-alert__action-group>.pf-c-button:not(:last-child){margin-right:var(--pf-c-alert__action-group__c-button--not-last-child--MarginRight)}.pf-m-overpass-font .pf-c-alert__title{--pf-c-alert__title--FontWeight:var(--pf-global--FontWeight--normal)}.pf-c-alert-group{--pf-c-alert-group__item--MarginTop:var(--pf-global--spacer--sm);--pf-c-alert-group--m-toast--Top:var(--pf-global--spacer--2xl);--pf-c-alert-group--m-toast--Right:var(--pf-global--spacer--xl);--pf-c-alert-group--m-toast--MaxWidth:37.5rem;--pf-c-alert-group--m-toast--ZIndex:var(--pf-global--ZIndex--2xl)}.pf-c-alert-group>*+*{margin-top:var(--pf-c-alert-group__item--MarginTop)}.pf-c-alert-group.pf-m-toast{position:fixed;top:var(--pf-c-alert-group--m-toast--Top);right:var(--pf-c-alert-group--m-toast--Right);z-index:var(--pf-c-alert-group--m-toast--ZIndex);width:calc(100% - var(--pf-c-alert-group--m-toast--Right)*2);max-width:var(--pf-c-alert-group--m-toast--MaxWidth)}.pf-c-app-launcher{--pf-c-app-launcher__menu--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-app-launcher__menu--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-app-launcher__menu--PaddingTop:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu--Top:calc(100% + var(--pf-global--spacer--xs));--pf-c-app-launcher__menu--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-app-launcher--m-top__menu--Top:0;--pf-c-app-launcher--m-top__menu--TranslateY:calc(-100% - var(--pf-global--spacer--xs));--pf-c-app-launcher__toggle--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-app-launcher__toggle--PaddingRight:var(--pf-global--spacer--md);--pf-c-app-launcher__toggle--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-app-launcher__toggle--PaddingLeft:var(--pf-global--spacer--md);--pf-c-app-launcher__toggle--Color:var(--pf-global--Color--200);--pf-c-app-launcher__toggle--hover--Color:var(--pf-global--Color--100);--pf-c-app-launcher__toggle--active--Color:var(--pf-global--Color--100);--pf-c-app-launcher__toggle--focus--Color:var(--pf-global--Color--100);--pf-c-app-launcher__toggle--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-app-launcher__toggle--m-expanded--Color:var(--pf-global--Color--100);--pf-c-app-launcher__menu-search--PaddingTop:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu-search--PaddingRight:var(--pf-global--spacer--md);--pf-c-app-launcher__menu-search--PaddingBottom:var(--pf-global--spacer--md);--pf-c-app-launcher__menu-search--PaddingLeft:var(--pf-global--spacer--md);--pf-c-app-launcher__menu-search--BottomBorderColor:var(--pf-global--BorderColor--100);--pf-c-app-launcher__menu-search--BottomBorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-app-launcher__menu-search--MarginBottom:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu-item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu-item--PaddingRight:var(--pf-global--spacer--md);--pf-c-app-launcher__menu-item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu-item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-app-launcher__menu-item--Color:var(--pf-global--Color--dark-100);--pf-c-app-launcher__menu-item--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-app-launcher__menu-item--Width:100%;--pf-c-app-launcher__menu-item--disabled--Color:var(--pf-global--Color--dark-200);--pf-c-app-launcher__menu-item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-app-launcher__menu-item--m-link--PaddingRight:0;--pf-c-app-launcher__menu-item--m-link--hover--BackgroundColor:transparent;--pf-c-app-launcher__menu-item--m-action--Color:var(--pf-global--disabled-color--200);--pf-c-app-launcher__menu-item--m-action--Width:auto;--pf-c-app-launcher__menu-item--m-action--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-app-launcher__menu-item--m-action--hover--BackgroundColor:transparent;--pf-c-app-launcher__menu-item--hover__menu-item--m-action--Color:var(--pf-global--Color--200);--pf-c-app-launcher__menu-item--m-action--hover--Color:var(--pf-global--Color--100);--pf-c-app-launcher__menu-item--m-favorite__menu-item--m-action--Color:var(--pf-global--palette--gold-400);--pf-c-app-launcher__menu-item-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-app-launcher__menu-item-icon--Width:var(--pf-global--icon--FontSize--lg);--pf-c-app-launcher__menu-item-icon--Height:var(--pf-global--icon--FontSize--lg);--pf-c-app-launcher__menu-item-external-icon--Color:var(--pf-global--link--Color);--pf-c-app-launcher__menu-item-external-icon--PaddingLeft:var(--pf-global--spacer--md);--pf-c-app-launcher__menu-item-external-icon--TranslateY:-0.0625rem;--pf-c-app-launcher__menu-item-external-icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-app-launcher__group--group--PaddingTop:var(--pf-global--spacer--sm);--pf-c-app-launcher__group-title--PaddingTop:var(--pf-global--spacer--sm);--pf-c-app-launcher__group-title--PaddingRight:var(--pf-c-app-launcher__menu-item--PaddingRight);--pf-c-app-launcher__group-title--PaddingBottom:var(--pf-c-app-launcher__menu-item--PaddingBottom);--pf-c-app-launcher__group-title--PaddingLeft:var(--pf-c-app-launcher__menu-item--PaddingLeft);--pf-c-app-launcher__group-title--FontSize:var(--pf-global--FontSize--sm);--pf-c-app-launcher__group-title--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-app-launcher__group-title--Color:var(--pf-global--Color--dark-200);--pf-c-app-launcher--c-divider--MarginTop:var(--pf-global--spacer--sm);--pf-c-app-launcher--c-divider--MarginBottom:var(--pf-global--spacer--sm);position:relative;display:inline-block;max-width:100%}.pf-c-app-launcher.pf-m-expanded>.pf-c-app-launcher__toggle{color:var(--pf-c-app-launcher__toggle--m-expanded--Color)}.pf-c-app-launcher .pf-c-divider{margin-top:var(--pf-c-app-launcher--c-divider--MarginTop);margin-bottom:var(--pf-c-app-launcher--c-divider--MarginBottom)}.pf-c-app-launcher .pf-c-divider:last-child{--pf-c-app-launcher--c-divider--MarginBottom:0}.pf-c-app-launcher__toggle{padding:var(--pf-c-app-launcher__toggle--PaddingTop) var(--pf-c-app-launcher__toggle--PaddingRight) var(--pf-c-app-launcher__toggle--PaddingBottom) var(--pf-c-app-launcher__toggle--PaddingLeft);color:var(--pf-c-app-launcher__toggle--Color);border:none}.pf-c-app-launcher__toggle:hover{--pf-c-app-launcher__toggle--Color:var(--pf-c-app-launcher__toggle--hover--Color)}.pf-c-app-launcher__toggle.pf-m-active,.pf-c-app-launcher__toggle:active{--pf-c-app-launcher__toggle--Color:var(--pf-c-app-launcher__toggle--active--Color)}.pf-c-app-launcher__toggle:focus{--pf-c-app-launcher__toggle--Color:var(--pf-c-app-launcher__toggle--focus--Color)}.pf-c-app-launcher__toggle:disabled{--pf-c-app-launcher__toggle--Color:var(--pf-c-app-launcher__toggle--disabled--Color);pointer-events:none}.pf-c-app-launcher__menu{position:absolute;top:var(--pf-c-app-launcher__menu--Top);z-index:var(--pf-c-app-launcher__menu--ZIndex);min-width:100%;padding-top:var(--pf-c-app-launcher__menu--PaddingTop);padding-bottom:var(--pf-c-app-launcher__menu--PaddingBottom);background-color:var(--pf-c-app-launcher__menu--BackgroundColor);background-clip:padding-box;box-shadow:var(--pf-c-app-launcher__menu--BoxShadow)}.pf-c-app-launcher__menu.pf-m-align-right{right:0}.pf-c-app-launcher.pf-m-top .pf-c-app-launcher__menu{--pf-c-app-launcher__menu--Top:var(--pf-c-app-launcher--m-top__menu--Top);transform:translateY(var(--pf-c-app-launcher--m-top__menu--TranslateY))}.pf-c-app-launcher__menu-search{padding:var(--pf-c-app-launcher__menu-search--PaddingTop) var(--pf-c-app-launcher__menu-search--PaddingRight) var(--pf-c-app-launcher__menu-search--PaddingBottom) var(--pf-c-app-launcher__menu-search--PaddingLeft);margin-bottom:var(--pf-c-app-launcher__menu-search--MarginBottom);border-bottom:var(--pf-c-app-launcher__menu-search--BottomBorderWidth) solid var(--pf-c-app-launcher__menu-search--BottomBorderColor)}.pf-c-app-launcher__menu-wrapper{display:flex}.pf-c-app-launcher__menu-wrapper.pf-m-favorite{--pf-c-app-launcher__menu-item--m-action--Color:var(--pf-c-app-launcher__menu-item--m-favorite__menu-item--m-action--Color)}.pf-c-app-launcher__menu-item{display:flex;align-items:center;width:var(--pf-c-app-launcher__menu-item--Width);padding:var(--pf-c-app-launcher__menu-item--PaddingTop) var(--pf-c-app-launcher__menu-item--PaddingRight) var(--pf-c-app-launcher__menu-item--PaddingBottom) var(--pf-c-app-launcher__menu-item--PaddingLeft);font-weight:var(--pf-c-app-launcher__menu-item--FontWeight);color:var(--pf-c-app-launcher__menu-item--Color);white-space:nowrap;border:0}.pf-c-app-launcher__menu-item:focus,.pf-c-app-launcher__menu-item:hover{--pf-c-app-launcher__menu-item--m-action--Color:var(--pf-c-app-launcher__menu-item--hover__menu-item--m-action--Color);text-decoration:none}.pf-c-app-launcher__menu-item:focus,.pf-c-app-launcher__menu-item:hover,.pf-c-app-launcher__menu-wrapper.pf-m-focus,.pf-c-app-launcher__menu-wrapper:focus-within,.pf-c-app-launcher__menu-wrapper:hover{background-color:var(--pf-c-app-launcher__menu-item--hover--BackgroundColor)}.pf-c-app-launcher__menu-item.pf-m-disabled,.pf-c-app-launcher__menu-item:disabled{--pf-c-app-launcher__menu-item--Color:var(--pf-c-app-launcher__menu-item--disabled--Color);pointer-events:none}.pf-c-app-launcher__menu-item.pf-m-disabled,.pf-c-app-launcher__menu-item:disabled,.pf-c-app-launcher__menu-wrapper.pf-m-disabled,.pf-c-app-launcher__menu-wrapper:disabled{background-color:transparent}.pf-c-app-launcher__menu-item.pf-m-external:focus .pf-c-app-launcher__menu-item-external-icon,.pf-c-app-launcher__menu-item.pf-m-external:hover .pf-c-app-launcher__menu-item-external-icon,.pf-c-app-launcher__menu-wrapper.pf-m-external:focus .pf-c-app-launcher__menu-item-external-icon,.pf-c-app-launcher__menu-wrapper.pf-m-external:hover .pf-c-app-launcher__menu-item-external-icon{opacity:1}.pf-c-app-launcher__menu-item.pf-m-link{--pf-c-app-launcher__menu-item--PaddingRight:var(--pf-c-app-launcher__menu-item--m-link--PaddingRight);--pf-c-app-launcher__menu-item--hover--BackgroundColor:var(--pf-c-app-launcher__menu-item--m-link--hover--BackgroundColor)}.pf-c-app-launcher__menu-item.pf-m-action{--pf-c-app-launcher__menu-item--Color:var(--pf-c-app-launcher__menu-item--m-action--Color);--pf-c-app-launcher__menu-item--Width:var(--pf-c-app-launcher__menu-item--m-action--Width);--pf-c-app-launcher__menu-item--hover--BackgroundColor:var(--pf-c-app-launcher__menu-item--m-action--hover--BackgroundColor);font-size:var(--pf-c-app-launcher__menu-item--m-action--FontSize)}.pf-c-app-launcher__menu-item.pf-m-action:focus,.pf-c-app-launcher__menu-item.pf-m-action:hover{--pf-c-app-launcher__menu-item--m-action--Color:var(--pf-c-app-launcher__menu-item--m-action--hover--Color)}.pf-c-app-launcher__menu-item-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--pf-c-app-launcher__menu-item-icon--Width);height:var(--pf-c-app-launcher__menu-item-icon--Height);margin-right:var(--pf-c-app-launcher__menu-item-icon--MarginRight)}.pf-c-app-launcher__menu-item-icon>*{max-width:100%;max-height:100%}.pf-c-app-launcher__menu-item-external-icon{padding-left:var(--pf-c-app-launcher__menu-item-external-icon--PaddingLeft);margin-left:auto;font-size:var(--pf-c-app-launcher__menu-item-external-icon--FontSize);color:var(--pf-c-app-launcher__menu-item-external-icon--Color);opacity:0;transform:translateY(var(--pf-c-app-launcher__menu-item-external-icon--TranslateY))}.pf-c-app-launcher__group+.pf-c-app-launcher__group{padding-top:var(--pf-c-app-launcher__group--group--PaddingTop)}.pf-c-app-launcher__group-title{padding:var(--pf-c-app-launcher__group-title--PaddingTop) var(--pf-c-app-launcher__group-title--PaddingRight) var(--pf-c-app-launcher__group-title--PaddingBottom) var(--pf-c-app-launcher__group-title--PaddingLeft);font-size:var(--pf-c-app-launcher__group-title--FontSize);font-weight:var(--pf-c-app-launcher__group-title--FontWeight);color:var(--pf-c-app-launcher__group-title--Color)}.pf-c-avatar{--pf-c-avatar--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-avatar--Width:2.25rem;--pf-c-avatar--Height:2.25rem;width:var(--pf-c-avatar--Width);height:var(--pf-c-avatar--Height);border-radius:var(--pf-c-avatar--BorderRadius)}.pf-c-backdrop{--pf-c-backdrop--ZIndex:var(--pf-global--ZIndex--lg);--pf-c-backdrop--BackgroundColor:var(--pf-global--BackgroundColor--dark-transparent-100);position:fixed;top:0;left:0;z-index:var(--pf-c-backdrop--ZIndex);width:100%;height:100%;background-color:var(--pf-c-backdrop--BackgroundColor)}.pf-c-backdrop__open{overflow:hidden}.pf-c-background-image{--pf-c-background-image--BackgroundColor:var(--pf-global--BackgroundColor--dark-100);--pf-c-background-image--BackgroundImage:url(assets/images/pfbg_576.jpg);--pf-c-background-image--BackgroundImage-2x:url(assets/images/pfbg_576@2x.jpg);--pf-c-background-image--BackgroundImage--sm:url(assets/images/pfbg_768.jpg);--pf-c-background-image--BackgroundImage--sm-2x:url(assets/images/pfbg_768@2x.jpg);--pf-c-background-image--BackgroundImage--lg:url(assets/images/pfbg_2000.jpg);--pf-c-background-image--Filter:url(#image_overlay)}.pf-c-background-image:before{position:fixed;top:0;left:0;z-index:-1;width:100%;height:100%;content:"";background-color:var(--pf-c-background-image--BackgroundColor);background-image:var(--pf-c-background-image--BackgroundImage);filter:var(--pf-c-background-image--Filter);background-repeat:no-repeat;background-size:cover}@media (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.pf-c-background-image:before{--pf-c-background-image--BackgroundImage:var(--pf-c-background-image--BackgroundImage-2x)}}@media (min-width:576px){.pf-c-background-image:before{--pf-c-background-image--BackgroundImage:var(--pf-c-background-image--BackgroundImage--sm)}}@media (min-width:576px) and (-webkit-min-device-pixel-ratio:2),(min-width:576px) and (min-resolution:192dpi){.pf-c-background-image:before{--pf-c-background-image--BackgroundImage:var(--pf-c-background-image--BackgroundImage--sm-2x)}}@media (min-width:992px){.pf-c-background-image:before{--pf-c-background-image--BackgroundImage:var(--pf-c-background-image--BackgroundImage--lg)}}.pf-c-background-image__filter{display:block}.pf-c-badge{--pf-c-badge--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-badge--FontSize:var(--pf-global--FontSize--xs);--pf-c-badge--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-badge--PaddingRight:var(--pf-global--spacer--sm);--pf-c-badge--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-badge--Color:var(--pf-global--Color--dark-100);--pf-c-badge--MinWidth:var(--pf-global--spacer--xl);--pf-c-badge--m-read--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-badge--m-read--Color:var(--pf-global--Color--dark-100);--pf-c-badge--m-unread--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-badge--m-unread--Color:var(--pf-global--Color--light-100);display:inline-block;min-width:var(--pf-c-badge--MinWidth);padding-right:var(--pf-c-badge--PaddingRight);padding-left:var(--pf-c-badge--PaddingLeft);font-size:var(--pf-c-badge--FontSize);font-weight:var(--pf-c-badge--FontWeight);color:var(--pf-c-badge--Color);text-align:center;background-color:var(--pf-c-badge--BackgroundColor);border-radius:var(--pf-c-badge--BorderRadius)}.pf-c-badge.pf-m-read{--pf-c-badge--Color:var(--pf-c-badge--m-read--Color);--pf-c-badge--BackgroundColor:var(--pf-c-badge--m-read--BackgroundColor)}.pf-c-badge.pf-m-unread{--pf-c-badge--Color:var(--pf-c-badge--m-unread--Color);--pf-c-badge--BackgroundColor:var(--pf-c-badge--m-unread--BackgroundColor)}.pf-c-banner{--pf-c-banner--PaddingTop:var(--pf-global--spacer--xs);--pf-c-banner--PaddingRight:var(--pf-global--spacer--md);--pf-c-banner--md--PaddingRight:var(--pf-global--spacer--lg);--pf-c-banner--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-banner--PaddingLeft:var(--pf-global--spacer--md);--pf-c-banner--md--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-banner--FontSize:var(--pf-global--FontSize--sm);--pf-c-banner--Color:var(--pf-global--Color--100);--pf-c-banner--BackgroundColor:var(--pf-global--BackgroundColor--dark-400);--pf-c-banner--m-info--BackgroundColor:var(--pf-global--palette--blue-200);--pf-c-banner--m-danger--BackgroundColor:var(--pf-global--danger-color--100);--pf-c-banner--m-success--BackgroundColor:var(--pf-global--success-color--100);--pf-c-banner--m-warning--BackgroundColor:var(--pf-global--warning-color--100);--pf-c-banner--m-sticky--ZIndex:var(--pf-global--ZIndex--md);--pf-c-banner--m-sticky--BoxShadow:var(--pf-global--BoxShadow--md-bottom);color:var(--pf-global--Color--100);overflow:hidden;text-overflow:ellipsis;padding:var(--pf-c-banner--PaddingTop) var(--pf-c-banner--PaddingRight) var(--pf-c-banner--PaddingBottom) var(--pf-c-banner--PaddingLeft);flex-shrink:0;font-size:var(--pf-c-banner--FontSize);color:var(--pf-c-banner--Color);white-space:nowrap;background-color:var(--pf-c-banner--BackgroundColor)}@media (min-width:768px){.pf-c-banner{--pf-c-banner--PaddingRight:var(--pf-c-banner--md--PaddingRight);--pf-c-banner--PaddingLeft:var(--pf-c-banner--md--PaddingLeft)}}.pf-c-banner.pf-m-info{color:var(--pf-global--Color--100);--pf-c-banner--BackgroundColor:var(--pf-c-banner--m-info--BackgroundColor)}.pf-c-banner.pf-m-danger{--pf-c-banner--BackgroundColor:var(--pf-c-banner--m-danger--BackgroundColor)}.pf-c-banner.pf-m-success{--pf-c-banner--BackgroundColor:var(--pf-c-banner--m-success--BackgroundColor)}.pf-c-banner.pf-m-warning{color:var(--pf-global--Color--100);--pf-c-banner--BackgroundColor:var(--pf-c-banner--m-warning--BackgroundColor)}.pf-c-banner.pf-m-sticky{position:sticky;top:0;z-index:var(--pf-c-banner--m-sticky--ZIndex);box-shadow:var(--pf-c-banner--m-sticky--BoxShadow)}.pf-c-breadcrumb{--pf-c-breadcrumb__item--FontSize:var(--pf-global--FontSize--sm);--pf-c-breadcrumb__item--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-breadcrumb__item--MarginRight:var(--pf-global--spacer--sm);--pf-c-breadcrumb__item-divider--Color:var(--pf-global--BorderColor--200);--pf-c-breadcrumb__item-divider--MarginRight:var(--pf-global--spacer--sm);--pf-c-breadcrumb__item-divider--FontSize:var(--pf-global--FontSize--sm);--pf-c-breadcrumb__link--m-current--Color:var(--pf-global--Color--100);--pf-c-breadcrumb__heading--FontSize:var(--pf-global--FontSize--sm);display:inline-flex}.pf-c-breadcrumb__list{display:flex;flex-wrap:wrap;align-items:center}.pf-c-breadcrumb__item{display:flex;align-items:baseline;font-size:var(--pf-c-breadcrumb__item--FontSize);font-weight:var(--pf-c-breadcrumb__item--FontWeight);line-height:var(--pf-c-breadcrumb__item--LineHeight);white-space:nowrap;list-style:none}.pf-c-breadcrumb__item:not(:last-child){margin-right:var(--pf-c-breadcrumb__item--MarginRight)}.pf-c-breadcrumb__item-divider{margin-right:var(--pf-c-breadcrumb__item-divider--MarginRight);font-size:var(--pf-c-breadcrumb__item-divider--FontSize);line-height:1;color:var(--pf-c-breadcrumb__item-divider--Color)}.pf-c-breadcrumb__link{font-size:inherit;font-weight:var(--pf-c-breadcrumb__link--FontWeight);line-height:inherit;word-break:break-word}.pf-c-breadcrumb__link.pf-m-current{cursor:default}.pf-c-breadcrumb__link.pf-m-current,.pf-c-breadcrumb__link.pf-m-current:hover{color:var(--pf-c-breadcrumb__link--m-current--Color);text-decoration:none}.pf-c-breadcrumb__heading{display:inline;font-size:var(--pf-c-breadcrumb__heading--FontSize)}.pf-c-breadcrumb__heading,.pf-c-breadcrumb__link{white-space:normal}.pf-m-overpass-font .pf-c-breadcrumb__item,.pf-m-overpass-font .pf-c-breadcrumb__link{font-weight:var(--pf-global--FontWeight--semi-bold)}.pf-c-breadcrumb__list>:first-child .pf-c-breadcrumb__item-divider{display:none;visibility:hidden}.pf-c-button{--pf-c-button--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-button--PaddingRight:var(--pf-global--spacer--md);--pf-c-button--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-button--PaddingLeft:var(--pf-global--spacer--md);--pf-c-button--LineHeight:var(--pf-global--LineHeight--md);--pf-c-button--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-button--FontSize:var(--pf-global--FontSize--md);--pf-c-button--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-button--after--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-button--after--BorderColor:transparent;--pf-c-button--after--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-button--hover--after--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-button--focus--after--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-button--active--after--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-button--disabled--Color:var(--pf-global--disabled-color--100);--pf-c-button--disabled--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-button--disabled--after--BorderColor:transparent;--pf-c-button--m-primary--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-button--m-primary--Color:var(--pf-global--Color--light-100);--pf-c-button--m-primary--hover--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-button--m-primary--hover--Color:var(--pf-global--Color--light-100);--pf-c-button--m-primary--focus--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-button--m-primary--focus--Color:var(--pf-global--Color--light-100);--pf-c-button--m-primary--active--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-button--m-primary--active--Color:var(--pf-global--Color--light-100);--pf-c-button--m-secondary--BackgroundColor:transparent;--pf-c-button--m-secondary--after--BorderColor:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--Color:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--hover--BackgroundColor:transparent;--pf-c-button--m-secondary--hover--after--BorderColor:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--hover--Color:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--focus--BackgroundColor:transparent;--pf-c-button--m-secondary--focus--after--BorderColor:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--focus--Color:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--active--BackgroundColor:transparent;--pf-c-button--m-secondary--active--after--BorderColor:var(--pf-global--primary-color--100);--pf-c-button--m-secondary--active--Color:var(--pf-global--primary-color--100);--pf-c-button--m-tertiary--BackgroundColor:transparent;--pf-c-button--m-tertiary--after--BorderColor:var(--pf-global--Color--100);--pf-c-button--m-tertiary--Color:var(--pf-global--Color--100);--pf-c-button--m-tertiary--hover--BackgroundColor:transparent;--pf-c-button--m-tertiary--hover--after--BorderColor:var(--pf-global--Color--100);--pf-c-button--m-tertiary--hover--Color:var(--pf-global--Color--100);--pf-c-button--m-tertiary--focus--BackgroundColor:transparent;--pf-c-button--m-tertiary--focus--after--BorderColor:var(--pf-global--Color--100);--pf-c-button--m-tertiary--focus--Color:var(--pf-global--Color--100);--pf-c-button--m-tertiary--active--BackgroundColor:transparent;--pf-c-button--m-tertiary--active--after--BorderColor:var(--pf-global--Color--100);--pf-c-button--m-tertiary--active--Color:var(--pf-global--Color--100);--pf-c-button--m-warning--BackgroundColor:var(--pf-global--warning-color--100);--pf-c-button--m-warning--Color:var(--pf-global--Color--dark-100);--pf-c-button--m-warning--hover--BackgroundColor:var(--pf-global--palette--gold-500);--pf-c-button--m-warning--hover--Color:var(--pf-global--Color--dark-100);--pf-c-button--m-warning--focus--BackgroundColor:var(--pf-global--palette--gold-500);--pf-c-button--m-warning--focus--Color:var(--pf-global--Color--dark-100);--pf-c-button--m-warning--active--BackgroundColor:var(--pf-global--palette--gold-500);--pf-c-button--m-warning--active--Color:var(--pf-global--Color--dark-100);--pf-c-button--m-danger--BackgroundColor:var(--pf-global--danger-color--100);--pf-c-button--m-danger--Color:var(--pf-global--Color--light-100);--pf-c-button--m-danger--hover--BackgroundColor:var(--pf-global--danger-color--200);--pf-c-button--m-danger--hover--Color:var(--pf-global--Color--light-100);--pf-c-button--m-danger--focus--BackgroundColor:var(--pf-global--danger-color--200);--pf-c-button--m-danger--focus--Color:var(--pf-global--Color--light-100);--pf-c-button--m-danger--active--BackgroundColor:var(--pf-global--danger-color--200);--pf-c-button--m-danger--active--Color:var(--pf-global--Color--light-100);--pf-c-button--m-link--BackgroundColor:transparent;--pf-c-button--m-link--Color:var(--pf-global--link--Color);--pf-c-button--m-link--hover--BackgroundColor:transparent;--pf-c-button--m-link--hover--Color:var(--pf-global--link--Color--hover);--pf-c-button--m-link--focus--BackgroundColor:transparent;--pf-c-button--m-link--focus--Color:var(--pf-global--link--Color--hover);--pf-c-button--m-link--active--BackgroundColor:transparent;--pf-c-button--m-link--active--Color:var(--pf-global--link--Color--hover);--pf-c-button--m-link--disabled--BackgroundColor:transparent;--pf-c-button--m-link--m-inline--FontSize:inherit;--pf-c-button--m-link--m-inline--hover--TextDecoration:var(--pf-global--link--TextDecoration--hover);--pf-c-button--m-link--m-inline--hover--Color:var(--pf-global--link--Color--hover);--pf-c-button--m-plain--BackgroundColor:transparent;--pf-c-button--m-plain--Color:var(--pf-global--Color--200);--pf-c-button--m-plain--hover--BackgroundColor:transparent;--pf-c-button--m-plain--hover--Color:var(--pf-global--Color--100);--pf-c-button--m-plain--focus--BackgroundColor:transparent;--pf-c-button--m-plain--focus--Color:var(--pf-global--Color--100);--pf-c-button--m-plain--active--BackgroundColor:transparent;--pf-c-button--m-plain--active--Color:var(--pf-global--Color--100);--pf-c-button--m-plain--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-button--m-plain--disabled--BackgroundColor:transparent;--pf-c-button--m-control--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-button--m-control--Color:var(--pf-global--Color--100);--pf-c-button--m-control--BorderRadius:0;--pf-c-button--m-control--after--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-button--m-control--after--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-button--m-control--after--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-button--m-control--after--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-button--m-control--after--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-button--m-control--disabled--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-button--m-control--hover--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-button--m-control--hover--Color:var(--pf-global--Color--100);--pf-c-button--m-control--hover--after--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-button--m-control--hover--after--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-button--m-control--active--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-button--m-control--active--Color:var(--pf-global--Color--100);--pf-c-button--m-control--active--after--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-button--m-control--active--after--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-button--m-control--focus--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-button--m-control--focus--Color:var(--pf-global--Color--100);--pf-c-button--m-control--focus--after--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-button--m-control--focus--after--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-button--m-control--m-expanded--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-button--m-control--m-expanded--Color:var(--pf-global--Color--100);--pf-c-button--m-control--m-expanded--after--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-button--m-control--m-expanded--after--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-button--m-small--FontSize:var(--pf-global--FontSize--sm);--pf-c-button--m-display-lg--PaddingTop:var(--pf-global--spacer--md);--pf-c-button--m-display-lg--PaddingRight:var(--pf-global--spacer--xl);--pf-c-button--m-display-lg--PaddingBottom:var(--pf-global--spacer--md);--pf-c-button--m-display-lg--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-button--m-display-lg--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-button--m-link--m-display-lg--FontSize:var(--pf-global--FontSize--lg);--pf-c-button__icon--m-start--MarginRight:var(--pf-global--spacer--xs);--pf-c-button__icon--m-end--MarginLeft:var(--pf-global--spacer--xs);--pf-c-button__progress--width:calc(var(--pf-global--icon--FontSize--md) + var(--pf-global--spacer--sm));--pf-c-button__progress--Opacity:0;--pf-c-button__progress--TranslateY:-50%;--pf-c-button__progress--Top:50%;--pf-c-button__progress--Left:var(--pf-global--spacer--md);--pf-c-button--m-progress--TransitionProperty:padding;--pf-c-button--m-progress--TransitionDuration:var(--pf-global--TransitionDuration);--pf-c-button--m-progress--PaddingRight:calc(var(--pf-global--spacer--md) + var(--pf-c-button__progress--width)/2);--pf-c-button--m-progress--PaddingLeft:calc(var(--pf-global--spacer--md) + var(--pf-c-button__progress--width)/2);--pf-c-button--m-in-progress--PaddingRight:var(--pf-global--spacer--md);--pf-c-button--m-in-progress--PaddingLeft:calc(var(--pf-global--spacer--md) + var(--pf-c-button__progress--width));position:relative;display:inline-block;padding:var(--pf-c-button--PaddingTop) var(--pf-c-button--PaddingRight) var(--pf-c-button--PaddingBottom) var(--pf-c-button--PaddingLeft);font-size:var(--pf-c-button--FontSize);font-weight:var(--pf-c-button--FontWeight);line-height:var(--pf-c-button--LineHeight);text-align:center;white-space:nowrap;user-select:none;border:0;border-radius:var(--pf-c-button--BorderRadius)}.pf-c-button:after{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;content:"";border:var(--pf-c-button--after--BorderWidth) solid;border-color:var(--pf-c-button--after--BorderColor);border-radius:var(--pf-c-button--after--BorderRadius)}.pf-c-button:hover{--pf-c-button--after--BorderWidth:var(--pf-c-button--hover--after--BorderWidth);text-decoration:none}.pf-c-button:focus{--pf-c-button--after--BorderWidth:var(--pf-c-button--focus--after--BorderWidth)}.pf-c-button.pf-m-active,.pf-c-button:active{--pf-c-button--after--BorderWidth:var(--pf-c-button--active--after--BorderWidth)}.pf-c-button.pf-m-block{display:block;width:100%}.pf-c-button.pf-m-small{--pf-c-button--FontSize:var(--pf-c-button--m-small--FontSize)}.pf-c-button.pf-m-link.pf-m-display-lg,.pf-c-button.pf-m-primary.pf-m-display-lg,.pf-c-button.pf-m-secondary.pf-m-display-lg,.pf-c-button.pf-m-tertiary.pf-m-display-lg{--pf-c-button--PaddingTop:var(--pf-c-button--m-display-lg--PaddingTop);--pf-c-button--PaddingRight:var(--pf-c-button--m-display-lg--PaddingRight);--pf-c-button--PaddingBottom:var(--pf-c-button--m-display-lg--PaddingBottom);--pf-c-button--PaddingLeft:var(--pf-c-button--m-display-lg--PaddingLeft);--pf-c-button--FontWeight:var(--pf-c-button--m-display-lg--FontWeight)}.pf-c-button.pf-m-primary{color:var(--pf-c-button--m-primary--Color);background-color:var(--pf-c-button--m-primary--BackgroundColor)}.pf-c-button.pf-m-primary:hover{--pf-c-button--m-primary--Color:var(--pf-c-button--m-primary--hover--Color);--pf-c-button--m-primary--BackgroundColor:var(--pf-c-button--m-primary--hover--BackgroundColor)}.pf-c-button.pf-m-primary:focus{--pf-c-button--m-primary--Color:var(--pf-c-button--m-primary--focus--Color);--pf-c-button--m-primary--BackgroundColor:var(--pf-c-button--m-primary--focus--BackgroundColor)}.pf-c-button.pf-m-primary.pf-m-active,.pf-c-button.pf-m-primary:active{--pf-c-button--m-primary--Color:var(--pf-c-button--m-primary--active--Color);--pf-c-button--m-primary--BackgroundColor:var(--pf-c-button--m-primary--active--BackgroundColor)}.pf-c-button.pf-m-secondary{--pf-c-button--after--BorderColor:var(--pf-c-button--m-secondary--after--BorderColor);color:var(--pf-c-button--m-secondary--Color);background-color:var(--pf-c-button--m-secondary--BackgroundColor)}.pf-c-button.pf-m-secondary:hover{--pf-c-button--m-secondary--Color:var(--pf-c-button--m-secondary--hover--Color);--pf-c-button--m-secondary--BackgroundColor:var(--pf-c-button--m-secondary--hover--BackgroundColor);--pf-c-button--after--BorderColor:var(--pf-c-button--m-secondary--hover--after--BorderColor)}.pf-c-button.pf-m-secondary:focus{--pf-c-button--m-secondary--Color:var(--pf-c-button--m-secondary--focus--Color);--pf-c-button--m-secondary--BackgroundColor:var(--pf-c-button--m-secondary--focus--BackgroundColor);--pf-c-button--after--BorderColor:var(--pf-c-button--m-secondary--focus--after--BorderColor)}.pf-c-button.pf-m-secondary.pf-m-active,.pf-c-button.pf-m-secondary:active{--pf-c-button--m-secondary--Color:var(--pf-c-button--m-secondary--active--Color);--pf-c-button--m-secondary--BackgroundColor:var(--pf-c-button--m-secondary--active--BackgroundColor);--pf-c-button--after--BorderColor:var(--pf-c-button--m-secondary--active--after--BorderColor)}.pf-c-button.pf-m-tertiary{--pf-c-button--after--BorderColor:var(--pf-c-button--m-tertiary--after--BorderColor);color:var(--pf-c-button--m-tertiary--Color);background-color:var(--pf-c-button--m-tertiary--BackgroundColor)}.pf-c-button.pf-m-tertiary:hover{--pf-c-button--m-tertiary--Color:var(--pf-c-button--m-tertiary--hover--Color);--pf-c-button--m-tertiary--BackgroundColor:var(--pf-c-button--m-tertiary--hover--BackgroundColor);--pf-c-button--after--BorderColor:var(--pf-c-button--m-tertiary--hover--after--BorderColor)}.pf-c-button.pf-m-tertiary:focus{--pf-c-button--m-tertiary--Color:var(--pf-c-button--m-tertiary--focus--Color);--pf-c-button--m-tertiary--BackgroundColor:var(--pf-c-button--m-tertiary--focus--BackgroundColor);--pf-c-button--after--BorderColor:var(--pf-c-button--m-tertiary--focus--after--BorderColor)}.pf-c-button.pf-m-tertiary.pf-m-active,.pf-c-button.pf-m-tertiary:active{--pf-c-button--m-tertiary--Color:var(--pf-c-button--m-tertiary--active--Color);--pf-c-button--m-tertiary--BackgroundColor:var(--pf-c-button--m-tertiary--active--BackgroundColor);--pf-c-button--after--BorderColor:var(--pf-c-button--m-tertiary--active--after--BorderColor)}.pf-c-button.pf-m-danger{color:var(--pf-c-button--m-danger--Color);background-color:var(--pf-c-button--m-danger--BackgroundColor)}.pf-c-button.pf-m-danger:hover{--pf-c-button--m-danger--Color:var(--pf-c-button--m-danger--hover--Color);--pf-c-button--m-danger--BackgroundColor:var(--pf-c-button--m-danger--hover--BackgroundColor)}.pf-c-button.pf-m-danger:focus{--pf-c-button--m-danger--Color:var(--pf-c-button--m-danger--focus--Color);--pf-c-button--m-danger--BackgroundColor:var(--pf-c-button--m-danger--focus--BackgroundColor)}.pf-c-button.pf-m-danger.pf-m-active,.pf-c-button.pf-m-danger:active{--pf-c-button--m-danger--Color:var(--pf-c-button--m-danger--active--Color);--pf-c-button--m-danger--BackgroundColor:var(--pf-c-button--m-danger--active--BackgroundColor)}.pf-c-button.pf-m-warning{color:var(--pf-c-button--m-warning--Color);background-color:var(--pf-c-button--m-warning--BackgroundColor)}.pf-c-button.pf-m-warning:hover{--pf-c-button--m-warning--Color:var(--pf-c-button--m-warning--hover--Color);--pf-c-button--m-warning--BackgroundColor:var(--pf-c-button--m-warning--hover--BackgroundColor)}.pf-c-button.pf-m-warning:focus{--pf-c-button--m-warning--Color:var(--pf-c-button--m-warning--focus--Color);--pf-c-button--m-warning--BackgroundColor:var(--pf-c-button--m-warning--focus--BackgroundColor)}.pf-c-button.pf-m-warning.pf-m-active,.pf-c-button.pf-m-warning:active{--pf-c-button--m-warning--Color:var(--pf-c-button--m-warning--active--Color);--pf-c-button--m-warning--BackgroundColor:var(--pf-c-button--m-warning--active--BackgroundColor)}.pf-c-button.pf-m-link{--pf-c-button--disabled--BackgroundColor:var(--pf-c-button--m-link--disabled--BackgroundColor);color:var(--pf-c-button--m-link--Color);background-color:var(--pf-c-button--m-link--BackgroundColor)}.pf-c-button.pf-m-link:not(.pf-m-inline):hover{--pf-c-button--m-link--Color:var(--pf-c-button--m-link--hover--Color);--pf-c-button--m-link--BackgroundColor:var(--pf-c-button--m-link--hover--BackgroundColor)}.pf-c-button.pf-m-link:not(.pf-m-inline):focus{--pf-c-button--m-link--Color:var(--pf-c-button--m-link--focus--Color);--pf-c-button--m-link--BackgroundColor:var(--pf-c-button--m-link--focus--BackgroundColor)}.pf-c-button.pf-m-link:not(.pf-m-inline).pf-m-active,.pf-c-button.pf-m-link:not(.pf-m-inline):active{--pf-c-button--m-link--Color:var(--pf-c-button--m-link--active--Color);--pf-c-button--m-link--BackgroundColor:var(--pf-c-button--m-link--active--BackgroundColor)}.pf-c-button.pf-m-link.pf-m-inline{--pf-c-button--FontSize:var(--pf-c-button--m-link--m-inline--FontSize);display:inline;padding:0;text-align:left;white-space:normal;cursor:pointer}.pf-c-button.pf-m-link.pf-m-inline:hover{--pf-c-button--m-link--Color:var(--pf-c-button--m-link--m-inline--hover--Color);text-decoration:var(--pf-c-button--m-link--m-inline--hover--TextDecoration)}.pf-c-button.pf-m-link.pf-m-display-lg{--pf-c-button--FontSize:var(--pf-c-button--m-link--m-display-lg--FontSize)}.pf-c-button.pf-m-control{--pf-c-button--BorderRadius:var(--pf-c-button--m-control--BorderRadius);--pf-c-button--disabled--BackgroundColor:var(--pf-c-button--m-control--disabled--BackgroundColor);--pf-c-button--after--BorderWidth:var(--pf-c-button--m-control--after--BorderWidth);--pf-c-button--after--BorderColor:var(--pf-c-button--m-control--after--BorderTopColor) var(--pf-c-button--m-control--after--BorderRightColor) var(--pf-c-button--m-control--after--BorderBottomColor) var(--pf-c-button--m-control--after--BorderLeftColor);color:var(--pf-c-button--m-control--Color);background-color:var(--pf-c-button--m-control--BackgroundColor)}.pf-c-button.pf-m-control:after{border-radius:initial}.pf-c-button.pf-m-control:hover{--pf-c-button--m-control--Color:var(--pf-c-button--m-control--hover--Color);--pf-c-button--m-control--BackgroundColor:var(--pf-c-button--m-control--hover--BackgroundColor);--pf-c-button--m-control--after--BorderBottomColor:var(--pf-c-button--m-control--hover--after--BorderBottomColor)}.pf-c-button.pf-m-control:hover:after{border-bottom-width:var(--pf-c-button--m-control--hover--after--BorderBottomWidth)}.pf-c-button.pf-m-control.pf-m-active,.pf-c-button.pf-m-control:active{--pf-c-button--m-control--Color:var(--pf-c-button--m-control--active--Color);--pf-c-button--m-control--BackgroundColor:var(--pf-c-button--m-control--active--BackgroundColor);--pf-c-button--m-control--after--BorderBottomColor:var(--pf-c-button--m-control--active--after--BorderBottomColor)}.pf-c-button.pf-m-control.pf-m-active:after,.pf-c-button.pf-m-control:active:after{border-bottom-width:var(--pf-c-button--m-control--active--after--BorderBottomWidth)}.pf-c-button.pf-m-control:focus{--pf-c-button--m-control--Color:var(--pf-c-button--m-control--focus--Color);--pf-c-button--m-control--BackgroundColor:var(--pf-c-button--m-control--focus--BackgroundColor);--pf-c-button--m-control--after--BorderBottomColor:var(--pf-c-button--m-control--focus--after--BorderBottomColor)}.pf-c-button.pf-m-control:focus:after{border-bottom-width:var(--pf-c-button--m-control--focus--after--BorderBottomWidth)}.pf-c-button.pf-m-control.pf-m-expanded{--pf-c-button--m-control--Color:var(--pf-c-button--m-control--m-expanded--Color);--pf-c-button--m-control--BackgroundColor:var(--pf-c-button--m-control--m-expanded--BackgroundColor);--pf-c-button--m-control--after--BorderBottomColor:var(--pf-c-button--m-control--m-expanded--after--BorderBottomColor)}.pf-c-button.pf-m-control.pf-m-expanded:after{border-bottom-width:var(--pf-c-button--m-control--m-expanded--after--BorderBottomWidth)}.pf-c-button.pf-m-plain{--pf-c-button--disabled--Color:var(--pf-c-button--m-plain--disabled--Color);--pf-c-button--disabled--BackgroundColor:var(--pf-c-button--m-plain--disabled--BackgroundColor);color:var(--pf-c-button--m-plain--Color);background-color:var(--pf-c-button--m-plain--BackgroundColor)}.pf-c-button.pf-m-plain:hover{--pf-c-button--m-plain--Color:var(--pf-c-button--m-plain--hover--Color);--pf-c-button--m-plain--BackgroundColor:var(--pf-c-button--m-plain--hover--BackgroundColor)}.pf-c-button.pf-m-plain.pf-m-active,.pf-c-button.pf-m-plain:active{--pf-c-button--m-plain--Color:var(--pf-c-button--m-plain--active--Color);--pf-c-button--m-plain--BackgroundColor:var(--pf-c-button--m-plain--active--BackgroundColor)}.pf-c-button.pf-m-plain:focus{--pf-c-button--m-plain--Color:var(--pf-c-button--m-plain--focus--Color);--pf-c-button--m-plain--BackgroundColor:var(--pf-c-button--m-plain--focus--BackgroundColor)}.pf-c-button.pf-m-disabled,.pf-c-button:disabled{pointer-events:none}.pf-c-button.pf-m-aria-disabled,.pf-c-button.pf-m-disabled,.pf-c-button:disabled{--pf-c-button--after--BorderColor:var(--pf-c-button--disabled--after--BorderColor);color:var(--pf-c-button--disabled--Color);background-color:var(--pf-c-button--disabled--BackgroundColor)}.pf-c-button.pf-m-aria-disabled{--pf-c-button--after--BorderWidth:0;--pf-c-button--m-link--m-inline--hover--TextDecoration:none;cursor:default}.pf-c-button.pf-m-progress{--pf-c-button--PaddingRight:var(--pf-c-button--m-progress--PaddingRight);--pf-c-button--PaddingLeft:var(--pf-c-button--m-progress--PaddingLeft);transition:var(--pf-c-button--m-progress--TransitionProperty) var(--pf-c-button--m-progress--TransitionDuration)}.pf-c-button.pf-m-in-progress{--pf-c-button--PaddingRight:var(--pf-c-button--m-in-progress--PaddingRight);--pf-c-button--PaddingLeft:var(--pf-c-button--m-in-progress--PaddingLeft)}.pf-c-button__icon.pf-m-start{margin-right:var(--pf-c-button__icon--m-start--MarginRight)}.pf-c-button__icon.pf-m-end{margin-left:var(--pf-c-button__icon--m-end--MarginLeft)}.pf-c-button__progress{position:absolute;top:var(--pf-c-button__progress--Top);left:var(--pf-c-button__progress--Left);line-height:1;transform:translateY(var(--pf-c-button__progress--TranslateY))}.pf-c-button__progress .pf-c-spinner{--pf-c-spinner--Color:currentColor}.pf-m-overpass-font .pf-c-button{--pf-c-button--FontWeight:var(--pf-global--FontWeight--semi-bold)}.pf-c-calendar-month{--pf-c-calendar-month--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-calendar-month--PaddingTop:var(--pf-global--spacer--lg);--pf-c-calendar-month--PaddingRight:var(--pf-global--spacer--lg);--pf-c-calendar-month--PaddingBottom:var(--pf-global--spacer--md);--pf-c-calendar-month--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-calendar-month--FontSize:var(--pf-global--FontSize--sm);--pf-c-calendar-month__header--MarginBottom:var(--pf-global--spacer--md);--pf-c-calendar-month__header-year--Width:8ch;--pf-c-calendar-month__header-nav-control--MarginRight:0;--pf-c-calendar-month__header-nav-control--MarginLeft:0;--pf-c-calendar-month__header-nav-control--m-prev-month--MarginRight:var(--pf-global--spacer--sm);--pf-c-calendar-month__header-nav-control--m-prev-month--MarginLeft:calc(var(--pf-global--spacer--md)*-1);--pf-c-calendar-month__header-nav-control--m-next-month--MarginRight:calc(var(--pf-global--spacer--md)*-1);--pf-c-calendar-month__header-nav-control--m-next-month--MarginLeft:var(--pf-global--spacer--sm);--pf-c-calendar-month__days--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-calendar-month__days--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-calendar-month__day--PaddingBottom:var(--pf-global--spacer--md);--pf-c-calendar-month__day--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-calendar-month__dates-cell--PaddingTop:0.125rem;--pf-c-calendar-month__dates-cell--PaddingRight:0.125rem;--pf-c-calendar-month__dates-cell--PaddingBottom:0.125rem;--pf-c-calendar-month__dates-cell--PaddingLeft:0.125rem;--pf-c-calendar-month__dates-row--first-child__dates-cell--PaddingTop:var(--pf-global--spacer--sm);--pf-c-calendar-month__dates-cell--m-current__date--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-calendar-month__dates-cell--m-selected__date--BackgroundColor:var(--pf-global--active-color--100);--pf-c-calendar-month__dates-cell--m-selected__date--hover--BackgroundColor:var(--pf-global--active-color--100);--pf-c-calendar-month__dates-cell--m-selected__date--focus--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-calendar-month__dates-cell--m-selected__date--focus--after--BorderColor:var(--pf-global--primary-color--200);--pf-c-calendar-month__date-cell--m-selected__date--focus--BoxShadow:0 0 0.3125rem var(--pf-global--primary-color--100);--pf-c-calendar-month__dates-cell--m-selected__date--Color:var(--pf-global--Color--light-100);--pf-c-calendar-month__dates-cell--before--BackgroundColor:transparent;--pf-c-calendar-month__dates-cell--before--Top:0;--pf-c-calendar-month__dates-cell--before--Right:0;--pf-c-calendar-month__dates-cell--before--Bottom:var(--pf-c-calendar-month__dates-cell--PaddingBottom);--pf-c-calendar-month__dates-cell--before--Left:0;--pf-c-calendar-month__dates-cell--m-in-range--before--BackgroundColor:var(--pf-global--palette--blue-50);--pf-c-calendar-month__dates-cell--m-in-range--m-start-range--before--Left:50%;--pf-c-calendar-month__dates-cell--m-in-range--m-end-range--before--Right:50%;--pf-c-calendar-month__dates-cell--m-in-range__date--hover--BackgroundColor:var(--pf-global--palette--blue-100);--pf-c-calendar-month__dates-cell--m-in-range__date--focus--BackgroundColor:var(--pf-global--palette--blue-100);--pf-c-calendar-month__dates-cell--m-adjacent-month__date--Color:var(--pf-global--disabled-color--100);--pf-c-calendar-month__date--Width:4ch;--pf-c-calendar-month__date--Height:4ch;--pf-c-calendar-month__date--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-calendar-month__date--Color:var(--pf-global--Color--100);--pf-c-calendar-month__date--BackgroundColor:transparent;--pf-c-calendar-month__date--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-calendar-month__date--after--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-calendar-month__date--after--BorderColor:transparent;--pf-c-calendar-month__date--hover--BackgroundColor:var(--pf-global--palette--blue-50);--pf-c-calendar-month__date--focus--BackgroundColor:var(--pf-global--palette--blue-50);--pf-c-calendar-month__date--focus--after--BorderColor:var(--pf-global--active-color--100);--pf-c-calendar-month__date--focus--BoxShadow:none;color:var(--pf-global--Color--100);display:inline-flex;flex-direction:column;padding:var(--pf-c-calendar-month--PaddingTop) var(--pf-c-calendar-month--PaddingRight) var(--pf-c-calendar-month--PaddingBottom) var(--pf-c-calendar-month--PaddingLeft);font-size:var(--pf-c-calendar-month--FontSize);background-color:var(--pf-c-calendar-month--BackgroundColor)}.pf-c-calendar-month__header{display:flex;margin-bottom:var(--pf-c-calendar-month__header--MarginBottom)}.pf-c-calendar-month__header-nav-control{margin-right:var(--pf-c-calendar-month__header-nav-control--MarginRight);margin-left:var(--pf-c-calendar-month__header-nav-control--MarginLeft)}.pf-c-calendar-month__header-nav-control.pf-m-prev-month{--pf-c-calendar-month__header-nav-control--MarginRight:var(--pf-c-calendar-month__header-nav-control--m-prev-month--MarginRight);--pf-c-calendar-month__header-nav-control--MarginLeft:var(--pf-c-calendar-month__header-nav-control--m-prev-month--MarginLeft)}.pf-c-calendar-month__header-nav-control.pf-m-next-month{--pf-c-calendar-month__header-nav-control--MarginRight:var(--pf-c-calendar-month__header-nav-control--m-next-month--MarginRight);--pf-c-calendar-month__header-nav-control--MarginLeft:var(--pf-c-calendar-month__header-nav-control--m-next-month--MarginLeft)}.pf-c-calendar-month__header-month{flex-grow:1}.pf-c-calendar-month__header-year{width:var(--pf-c-calendar-month__header-year--Width)}.pf-c-calendar-month__calendar{table-layout:fixed}.pf-c-calendar-month__days{border-bottom:var(--pf-c-calendar-month__days--BorderBottomWidth) solid var(--pf-c-calendar-month__days--BorderBottomColor)}.pf-c-calendar-month__day{padding-bottom:var(--pf-c-calendar-month__day--PaddingBottom);font-weight:var(--pf-c-calendar-month__day--FontWeight);text-align:center}.pf-c-calendar-month__dates-row:first-child{--pf-c-calendar-month__dates-cell--PaddingTop:var(--pf-c-calendar-month__dates-row--first-child__dates-cell--PaddingTop)}.pf-c-calendar-month__dates-cell{--pf-c-calendar-month__dates-cell--before--Top:var(--pf-c-calendar-month__dates-cell--PaddingTop);position:relative;padding:var(--pf-c-calendar-month__dates-cell--PaddingTop) var(--pf-c-calendar-month__dates-cell--PaddingRight) var(--pf-c-calendar-month__dates-cell--PaddingBottom) var(--pf-c-calendar-month__dates-cell--PaddingLeft);text-align:center}.pf-c-calendar-month__dates-cell:before{position:absolute;top:var(--pf-c-calendar-month__dates-cell--before--Top);right:var(--pf-c-calendar-month__dates-cell--before--Right);bottom:var(--pf-c-calendar-month__dates-cell--before--Bottom);left:var(--pf-c-calendar-month__dates-cell--before--Left);content:"";background-color:var(--pf-c-calendar-month__dates-cell--before--BackgroundColor)}.pf-c-calendar-month__dates-cell.pf-m-current{--pf-c-calendar-month__date--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-current__date--BackgroundColor)}.pf-c-calendar-month__dates-cell.pf-m-in-range{--pf-c-calendar-month__dates-cell--before--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-in-range--before--BackgroundColor);--pf-c-calendar-month__date--hover--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-in-range__date--hover--BackgroundColor);--pf-c-calendar-month__date--focus--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-in-range__date--focus--BackgroundColor)}.pf-c-calendar-month__dates-cell.pf-m-start-range{--pf-c-calendar-month__dates-cell--before--Left:var(--pf-c-calendar-month__dates-cell--m-in-range--m-start-range--before--Left)}.pf-c-calendar-month__dates-cell.pf-m-end-range{--pf-c-calendar-month__dates-cell--before--Right:var(--pf-c-calendar-month__dates-cell--m-in-range--m-end-range--before--Right)}.pf-c-calendar-month__dates-cell.pf-m-adjacent-month{--pf-c-calendar-month__date--Color:var(--pf-c-calendar-month__dates-cell--m-adjacent-month__date--Color)}.pf-c-calendar-month__dates-cell.pf-m-selected{--pf-c-calendar-month__date--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-selected__date--BackgroundColor);--pf-c-calendar-month__date--hover--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-selected__date--hover--BackgroundColor);--pf-c-calendar-month__date--focus--BackgroundColor:var(--pf-c-calendar-month__dates-cell--m-selected__date--focus--BackgroundColor);--pf-c-calendar-month__date--focus--after--BorderColor:var(--pf-c-calendar-month__dates-cell--m-selected__date--focus--after--BorderColor);--pf-c-calendar-month__date--focus--BoxShadow:var(--pf-c-calendar-month__date-cell--m-selected__date--focus--BoxShadow);--pf-c-calendar-month__date--Color:var(--pf-c-calendar-month__dates-cell--m-selected__date--Color)}.pf-c-calendar-month__dates-cell.pf-m-disabled{--pf-c-calendar-month__dates-cell--before--BackgroundColor:transparent;--pf-c-calendar-month__date--BackgroundColor:transparent}.pf-c-calendar-month__date{position:relative;display:inline-flex;align-items:center;justify-content:center;width:var(--pf-c-calendar-month__date--Width);height:var(--pf-c-calendar-month__date--Height);line-height:1;color:var(--pf-c-calendar-month__date--Color);background-color:var(--pf-c-calendar-month__date--BackgroundColor);border:0}.pf-c-calendar-month__date:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-calendar-month__date--after--BorderWidth) solid var(--pf-c-calendar-month__date--after--BorderColor)}.pf-c-calendar-month__date,.pf-c-calendar-month__date:after{border-radius:var(--pf-c-calendar-month__date--BorderRadius)}.pf-c-calendar-month__date.pf-m-hover,.pf-c-calendar-month__date:hover{--pf-c-calendar-month__date--BackgroundColor:var(--pf-c-calendar-month__date--hover--BackgroundColor)}.pf-c-calendar-month__date.pf-m-focus,.pf-c-calendar-month__date:focus{--pf-c-calendar-month__date--BackgroundColor:var(--pf-c-calendar-month__date--focus--BackgroundColor);--pf-c-calendar-month__date--after--BorderColor:var(--pf-c-calendar-month__date--focus--after--BorderColor);outline:0;box-shadow:var(--pf-c-calendar-month__date--focus--BoxShadow)}.pf-c-calendar-month__date:disabled{pointer-events:none;--pf-c-calendar-month__date--Color:var(--pf-c-calendar-month__date--disabled--Color);--pf-c-calendar-month__date--hover--focus--BorderColor:transparent}.pf-c-card{--pf-c-card--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-card--BoxShadow:var(--pf-global--BoxShadow--sm);--pf-c-card--m-hoverable--hover--BoxShadow:var(--pf-global--BoxShadow--lg);--pf-c-card--m-selectable--hover--BoxShadow:var(--pf-global--BoxShadow--lg);--pf-c-card--m-selectable--focus--BoxShadow:var(--pf-global--BoxShadow--lg);--pf-c-card--m-selectable--active--BoxShadow:var(--pf-global--BoxShadow--lg);--pf-c-card--m-selectable--m-selected--BoxShadow:var(--pf-global--BoxShadow--lg);--pf-c-card--m-selectable--m-selected--before--Height:var(--pf-global--BorderWidth--lg);--pf-c-card--m-selectable--m-selected--before--BackgroundColor:var(--pf-global--active-color--100);--pf-c-card--m-compact__body--FontSize:var(--pf-global--FontSize--sm);--pf-c-card--m-compact__footer--FontSize:var(--pf-global--FontSize--sm);--pf-c-card--m-compact--first-child--PaddingTop:var(--pf-global--spacer--md);--pf-c-card--m-compact--child--PaddingRight:var(--pf-global--spacer--md);--pf-c-card--m-compact--child--PaddingBottom:var(--pf-global--spacer--md);--pf-c-card--m-compact--child--PaddingLeft:var(--pf-global--spacer--md);--pf-c-card--m-compact--c-divider--child--PaddingTop:var(--pf-global--spacer--md);--pf-c-card--m-compact__title--not--last-child--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-card--m-flat--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-card--m-flat--BorderColor:var(--pf-global--BorderColor--100);--pf-c-card--first-child--PaddingTop:var(--pf-global--spacer--lg);--pf-c-card--child--PaddingRight:var(--pf-global--spacer--lg);--pf-c-card--child--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-card--child--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-card--c-divider--child--PaddingTop:var(--pf-global--spacer--lg);--pf-c-card__header-toggle--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-card__header-toggle--MarginRight:var(--pf-global--spacer--xs);--pf-c-card__header-toggle--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-card__header-toggle--MarginLeft:calc(var(--pf-global--spacer--md)*-1);--pf-c-card__header-toggle-icon--Transition:var(--pf-global--Transition);--pf-c-card--m-expanded__header-toggle-icon--Rotate:90deg;--pf-c-card__title--FontSize:var(--pf-global--FontSize--md);--pf-c-card__title--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-card__title--not--last-child--PaddingBottom:var(--pf-global--spacer--md);--pf-c-card__body--FontSize:var(--pf-global--FontSize--md);--pf-c-card__footer--FontSize:var(--pf-global--FontSize--md);--pf-c-card__actions--PaddingLeft:var(--pf-global--spacer--md);--pf-c-card__actions--child--MarginLeft:var(--pf-global--spacer--sm);display:flex;flex-direction:column;background-color:var(--pf-c-card--BackgroundColor);box-shadow:var(--pf-c-card--BoxShadow)}.pf-c-card.pf-m-hoverable:hover{box-shadow:var(--pf-c-card--m-hoverable--hover--BoxShadow)}.pf-c-card.pf-m-selectable{position:relative;cursor:pointer}.pf-c-card.pf-m-selectable:hover{box-shadow:var(--pf-c-card--m-selectable--hover--BoxShadow)}.pf-c-card.pf-m-selectable:focus{box-shadow:var(--pf-c-card--m-selectable--focus--BoxShadow)}.pf-c-card.pf-m-selectable:active{box-shadow:var(--pf-c-card--m-selectable--active--BoxShadow)}.pf-c-card.pf-m-selectable.pf-m-selected{box-shadow:var(--pf-c-card--m-selectable--m-selected--BoxShadow)}.pf-c-card.pf-m-selectable.pf-m-selected:before{position:absolute;top:0;right:0;left:0;height:var(--pf-c-card--m-selectable--m-selected--before--Height);content:"";background-color:var(--pf-c-card--m-selectable--m-selected--before--BackgroundColor)}.pf-c-card.pf-m-compact{--pf-c-card__body--FontSize:var(--pf-c-card--m-compact__body--FontSize);--pf-c-card__footer--FontSize:var(--pf-c-card--m-compact__footer--FontSize);--pf-c-card--first-child--PaddingTop:var(--pf-c-card--m-compact--first-child--PaddingTop);--pf-c-card--child--PaddingRight:var(--pf-c-card--m-compact--child--PaddingRight);--pf-c-card--child--PaddingBottom:var(--pf-c-card--m-compact--child--PaddingBottom);--pf-c-card--child--PaddingLeft:var(--pf-c-card--m-compact--child--PaddingLeft);--pf-c-card--c-divider--child--PaddingTop:var(--pf-c-card--m-compact--c-divider--child--PaddingTop);--pf-c-card__title--not--last-child--PaddingBottom:var(--pf-c-card--m-compact__title--not--last-child--PaddingBottom)}.pf-c-card.pf-m-flat{--pf-c-card--BoxShadow:none;border:var(--pf-c-card--m-flat--BorderWidth) solid var(--pf-c-card--m-flat--BorderColor)}.pf-c-card.pf-m-expanded .pf-c-card__header-toggle-icon{transform:rotate(var(--pf-c-card--m-expanded__header-toggle-icon--Rotate))}.pf-c-card>.pf-c-divider+.pf-c-card__body,.pf-c-card>.pf-c-divider+.pf-c-card__footer,.pf-c-card>.pf-c-divider+.pf-c-card__header,.pf-c-card>.pf-c-divider+.pf-c-card__title{padding-top:var(--pf-c-card--c-divider--child--PaddingTop)}.pf-c-card__header{display:flex;flex-direction:row;align-items:center}.pf-c-card__header .pf-c-card__title{padding:0}.pf-c-card__header-toggle{align-self:flex-start;margin:var(--pf-c-card__header-toggle--MarginTop) var(--pf-c-card__header-toggle--MarginRight) var(--pf-c-card__header-toggle--MarginBottom) var(--pf-c-card__header-toggle--MarginLeft)}.pf-c-card__header-toggle-icon{display:inline-block;transition:var(--pf-c-card__header-toggle-icon--Transition)}.pf-c-card__title{font-family:var(--pf-c-card__title--FontFamily);font-weight:var(--pf-c-card__title--FontWeight)}.pf-c-card__actions{display:flex;align-items:center;align-self:flex-start;order:1;padding-left:var(--pf-c-card__actions--PaddingLeft);margin:var(--pf-c-card__header-toggle--MarginTop) var(--pf-c-card__header-toggle--MarginRight) var(--pf-c-card__header-toggle--MarginBottom) auto}.pf-c-card__actions>*+*{margin-left:var(--pf-c-card__actions--child--MarginLeft)}.pf-c-card__actions+.pf-c-card__body,.pf-c-card__actions+.pf-c-card__footer,.pf-c-card__actions+.pf-c-card__title{padding:0}.pf-c-card__body,.pf-c-card__footer,.pf-c-card__header,.pf-c-card__title{padding-right:var(--pf-c-card--child--PaddingRight);padding-bottom:var(--pf-c-card--child--PaddingBottom);padding-left:var(--pf-c-card--child--PaddingLeft)}.pf-c-card__body:first-child,.pf-c-card__footer:first-child,.pf-c-card__header:first-child,.pf-c-card__title:first-child{padding-top:var(--pf-c-card--first-child--PaddingTop)}.pf-c-card__header:not(:last-child),.pf-c-card__title:not(:last-child){padding-bottom:var(--pf-c-card__title--not--last-child--PaddingBottom)}.pf-c-card__expandable-content{--pf-c-card--first-child--PaddingTop:0}.pf-c-card__body:not(.pf-m-no-fill){flex:1 1 auto}.pf-c-card__body{font-size:var(--pf-c-card__body--FontSize)}.pf-c-card__footer{font-size:var(--pf-c-card__footer--FontSize)}.pf-m-overpass-font .pf-c-card .pf-c-card__title{font-weight:var(--pf-global--FontWeight--normal)}.pf-c-check{--pf-c-check--GridGap:var(--pf-global--spacer--xs) var(--pf-global--spacer--sm);--pf-c-check__label--disabled--Color:var(--pf-global--disabled-color--100);--pf-c-check__label--Color:var(--pf-global--Color--100);--pf-c-check__label--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-check__label--FontSize:var(--pf-global--FontSize--md);--pf-c-check__label--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-check__input--MarginTop:-0.1875rem;--pf-c-check__description--FontSize:var(--pf-global--FontSize--sm);--pf-c-check__description--Color:var(--pf-global--Color--200);display:grid;grid-template-columns:auto 1fr;grid-gap:var(--pf-c-check--GridGap);align-items:center;justify-items:start}.pf-c-check__label{font-size:var(--pf-c-check__label--FontSize);font-weight:var(--pf-c-check__label--FontWeight);line-height:var(--pf-c-check__label--LineHeight);color:var(--pf-c-check__label--Color)}.pf-c-check__input{margin-top:var(--pf-c-check__input--MarginTop)}.pf-c-check__description{grid-column:2;font-size:var(--pf-c-check__description--FontSize);color:var(--pf-c-check__description--Color)}.pf-c-check__input,.pf-c-check__label,label.pf-c-check{cursor:pointer}.pf-c-check__input.pf-m-disabled,.pf-c-check__input:disabled,.pf-c-check__label.pf-m-disabled,.pf-c-check__label:disabled{--pf-c-check__label--Color:var(--pf-c-check__label--disabled--Color);cursor:not-allowed}.pf-c-chip{--pf-c-chip--PaddingTop:var(--pf-global--spacer--xs);--pf-c-chip--PaddingRight:var(--pf-global--spacer--sm);--pf-c-chip--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-chip--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-chip--BackgroundColor:var(--pf-global--Color--light-100);--pf-c-chip--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-chip--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-chip--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-chip--before--BorderRadius:var(--pf-c-chip--BorderRadius);--pf-c-chip--m-overflow__text--Color:var(--pf-global--primary-color--100);--pf-c-chip--m-draggable--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-chip--m-draggable--BoxShadow:var(--pf-global--BoxShadow--sm);--pf-c-chip--m-draggable__icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-chip__text--FontSize:var(--pf-global--FontSize--xs);--pf-c-chip__text--Color:var(--pf-global--Color--100);--pf-c-chip__text--MaxWidth:16ch;--pf-c-chip__c-button--PaddingTop:var(--pf-global--spacer--xs);--pf-c-chip__c-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-chip__c-button--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-chip__c-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-chip__c-button--MarginTop:calc(var(--pf-c-chip--PaddingTop)*-1);--pf-c-chip__c-button--MarginRight:calc(var(--pf-c-chip--PaddingRight)/2*-1);--pf-c-chip__c-button--MarginBottom:calc(var(--pf-c-chip--PaddingBottom)*-1);--pf-c-chip__c-button--FontSize:var(--pf-global--FontSize--xs);--pf-c-chip__c-badge--MarginLeft:var(--pf-global--spacer--xs);--pf-c-chip__icon--MarginLeft:var(--pf-global--spacer--sm);color:var(--pf-global--Color--100);position:relative;display:inline-flex;align-items:center;padding:var(--pf-c-chip--PaddingTop) var(--pf-c-chip--PaddingRight) var(--pf-c-chip--PaddingBottom) var(--pf-c-chip--PaddingLeft);list-style:none;background-color:var(--pf-c-chip--BackgroundColor);border-radius:var(--pf-c-chip--BorderRadius)}.pf-c-chip:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-chip--before--BorderWidth) solid var(--pf-c-chip--before--BorderColor);border-radius:var(--pf-c-chip--before--BorderRadius)}.pf-c-chip.pf-m-overflow{border:0}.pf-c-chip.pf-m-overflow .pf-c-chip__text{color:var(--pf-c-chip--m-overflow__text--Color)}.pf-c-chip.pf-m-draggable{--pf-c-chip--BackgroundColor:var(--pf-c-chip--m-draggable--BackgroundColor);box-shadow:var(--pf-c-chip--m-draggable--BoxShadow)}.pf-c-chip.pf-m-draggable .pf-c-chip__icon{font-size:var(--pf-c-chip--m-draggable__icon--FontSize)}.pf-c-chip .pf-c-button{--pf-c-button--PaddingTop:var(--pf-c-chip__c-button--PaddingTop);--pf-c-button--PaddingRight:var(--pf-c-chip__c-button--PaddingRight);--pf-c-button--PaddingBottom:var(--pf-c-chip__c-button--PaddingBottom);--pf-c-button--PaddingLeft:var(--pf-c-chip__c-button--PaddingLeft);--pf-c-button--FontSize:var(--pf-c-chip__c-button--FontSize);margin-top:var(--pf-c-chip__c-button--MarginTop);margin-right:var(--pf-c-chip__c-button--MarginRight);margin-bottom:var(--pf-c-chip__c-button--MarginBottom)}.pf-c-chip .pf-c-badge{margin-left:var(--pf-c-chip__c-badge--MarginLeft)}.pf-c-chip__text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:relative;max-width:var(--pf-c-chip__text--MaxWidth);font-size:var(--pf-c-chip__text--FontSize);color:var(--pf-c-chip__text--Color)}.pf-c-chip__icon+.pf-c-chip__text,.pf-c-chip__text+.pf-c-chip__icon{margin-left:var(--pf-c-chip__icon--MarginLeft)}.pf-c-chip-group{color:var(--pf-global--Color--100);--pf-c-chip-group__list--MarginBottom:calc(var(--pf-global--spacer--xs)*-1);--pf-c-chip-group__list--MarginRight:calc(var(--pf-global--spacer--xs)*-1);--pf-c-chip-group--m-category--PaddingTop:var(--pf-global--spacer--xs);--pf-c-chip-group--m-category--PaddingRight:var(--pf-global--spacer--xs);--pf-c-chip-group--m-category--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-chip-group--m-category--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-chip-group--m-category--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-chip-group--m-category--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-chip-group__label--MarginRight:var(--pf-global--spacer--sm);--pf-c-chip-group__label--FontSize:var(--pf-global--FontSize--sm);--pf-c-chip-group__label--MaxWidth:18ch;--pf-c-chip-group__close--MarginTop:calc(var(--pf-global--spacer--xs)*-1);--pf-c-chip-group__close--MarginBottom:calc(var(--pf-global--spacer--xs)*-1);--pf-c-chip-group__list-item--MarginRight:var(--pf-global--spacer--xs);--pf-c-chip-group__list-item--MarginBottom:var(--pf-global--spacer--xs)}.pf-c-chip-group.pf-m-category{padding:var(--pf-c-chip-group--m-category--PaddingTop) var(--pf-c-chip-group--m-category--PaddingRight) var(--pf-c-chip-group--m-category--PaddingBottom) var(--pf-c-chip-group--m-category--PaddingLeft);background-color:var(--pf-c-chip-group--m-category--BackgroundColor);border-radius:var(--pf-c-chip-group--m-category--BorderRadius)}.pf-c-chip-group__main{display:flex;flex:1;flex-wrap:wrap;align-items:baseline}.pf-c-chip-group__list{margin-right:var(--pf-c-chip-group__list--MarginRight);margin-bottom:var(--pf-c-chip-group__list--MarginBottom)}.pf-c-chip-group,.pf-c-chip-group__list{display:inline-flex;flex-wrap:wrap;align-items:center}.pf-c-chip-group__list-item{display:inline-flex;margin-right:var(--pf-c-chip-group__list-item--MarginRight);margin-bottom:var(--pf-c-chip-group__list-item--MarginBottom)}.pf-c-chip-group__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:var(--pf-c-chip-group__label--MaxWidth);margin-right:var(--pf-c-chip-group__label--MarginRight);font-size:var(--pf-c-chip-group__label--FontSize)}.pf-c-chip-group__close{display:flex;align-self:flex-start;margin-top:var(--pf-c-chip-group__close--MarginTop);margin-bottom:var(--pf-c-chip-group__close--MarginBottom)}.pf-c-clipboard-copy{--pf-c-clipboard-copy__toggle-icon--Transition:.2s ease-in 0s;--pf-c-clipboard-copy--m-expanded__toggle-icon--Rotate:90deg;--pf-c-clipboard-copy__expandable-content--PaddingTop:var(--pf-global--spacer--md);--pf-c-clipboard-copy__expandable-content--PaddingRight:var(--pf-global--spacer--md);--pf-c-clipboard-copy__expandable-content--PaddingBottom:var(--pf-global--spacer--md);--pf-c-clipboard-copy__expandable-content--PaddingLeft:var(--pf-global--spacer--md);--pf-c-clipboard-copy__expandable-content--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-clipboard-copy__expandable-content--BorderTopWidth:0;--pf-c-clipboard-copy__expandable-content--BorderRightWidth:var(--pf-global--BorderWidth--sm);--pf-c-clipboard-copy__expandable-content--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-clipboard-copy__expandable-content--BorderLeftWidth:var(--pf-global--BorderWidth--sm);--pf-c-clipboard-copy__expandable-content--BorderColor:var(--pf-global--BorderColor--100);--pf-c-clipboard-copy__expandable-content--OutlineOffset:calc(-1*var(--pf-global--spacer--xs))}.pf-c-clipboard-copy.pf-m-expanded .pf-c-clipboard-copy__toggle-icon{transform:rotate(var(--pf-c-clipboard-copy--m-expanded__toggle-icon--Rotate))}.pf-c-clipboard-copy__group{display:flex}.pf-c-clipboard-copy__group>*+*{margin-left:-1px}.pf-c-clipboard-copy__toggle-icon{transition:var(--pf-c-clipboard-copy__toggle-icon--Transition)}.pf-c-clipboard-copy__expandable-content{padding:var(--pf-c-clipboard-copy__expandable-content--PaddingTop) var(--pf-c-clipboard-copy__expandable-content--PaddingRight) var(--pf-c-clipboard-copy__expandable-content--PaddingBottom) var(--pf-c-clipboard-copy__expandable-content--PaddingLeft);word-wrap:break-word;background-color:var(--pf-c-clipboard-copy__expandable-content--BackgroundColor);background-clip:padding-box;border:solid var(--pf-c-clipboard-copy__expandable-content--BorderColor);border-width:var(--pf-c-clipboard-copy__expandable-content--BorderTopWidth) var(--pf-c-clipboard-copy__expandable-content--BorderRightWidth) var(--pf-c-clipboard-copy__expandable-content--BorderBottomWidth) var(--pf-c-clipboard-copy__expandable-content--BorderLeftWidth);box-shadow:var(--pf-c-clipboard-copy__expandable-content--BoxShadow)}.pf-c-clipboard-copy__expandable-content pre{white-space:pre-wrap}.pf-c-code-editor{--pf-c-code-editor__controls--c-button--m-control--Color:var(--pf-global--Color--200);--pf-c-code-editor__controls--c-button--m-control--hover--Color:var(--pf-global--Color--100);--pf-c-code-editor__controls--c-button--m-control--focus--Color:var(--pf-global--Color--100);--pf-c-code-editor__controls--c-button--m-control--disabled--after--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-code-editor__header--before--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-code-editor__header--before--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-code-editor__main--BorderColor:var(--pf-global--BorderColor--100);--pf-c-code-editor__main--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-code-editor__main--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-code-editor--m-read-only__main--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-code-editor__main--m-drag-hover--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-code-editor__main--m-drag-hover--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-code-editor__main--m-drag-hover--after--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-code-editor__main--m-drag-hover--after--Opacity:.1;--pf-c-code-editor__code--PaddingTop:var(--pf-global--spacer--sm);--pf-c-code-editor__code--PaddingRight:var(--pf-global--spacer--sm);--pf-c-code-editor__code--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-code-editor__code--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-code-editor__code-pre--FontSize:var(--pf-global--FontSize--sm);--pf-c-code-editor__code-pre--FontFamily:var(--pf-global--FontFamily--monospace);--pf-c-code-editor__tab--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-code-editor__tab--Color:var(--pf-global--Color--200);--pf-c-code-editor__tab--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-code-editor__tab--PaddingRight:var(--pf-global--spacer--sm);--pf-c-code-editor__tab--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-code-editor__tab--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-code-editor__tab--BorderTopWidth:var(--pf-global--BorderWidth--sm);--pf-c-code-editor__tab--BorderRightWidth:var(--pf-global--BorderWidth--sm);--pf-c-code-editor__tab--BorderBottomWidth:0;--pf-c-code-editor__tab--BorderLeftWidth:var(--pf-global--BorderWidth--sm);--pf-c-code-editor__tab--BorderColor:var(--pf-global--BorderColor--100);--pf-c-code-editor__tab-icon--text--MarginLeft:var(--pf-global--spacer--sm)}.pf-c-code-editor.pf-m-read-only{--pf-c-code-editor__main--BackgroundColor:var(--pf-c-code-editor--m-read-only__main--BackgroundColor)}.pf-c-code-editor__header{position:relative;display:flex;align-items:flex-end}.pf-c-code-editor__header:before{position:absolute;right:0;bottom:0;left:0;pointer-events:none;content:"";border-bottom:var(--pf-c-code-editor__header--before--BorderBottomWidth) solid var(--pf-c-code-editor__header--before--BorderBottomColor)}.pf-c-code-editor__controls{display:flex}.pf-c-code-editor__controls .pf-c-button.pf-m-control{--pf-c-button--m-control--Color:var(--pf-c-code-editor__controls--c-button--m-control--Color)}.pf-c-code-editor__controls .pf-c-button.pf-m-control:hover{--pf-c-code-editor__controls--c-button--m-control--Color:var(--pf-c-code-editor__controls--c-button--m-control--hover--Color)}.pf-c-code-editor__controls .pf-c-button.pf-m-control:focus{--pf-c-code-editor__controls--c-button--m-control--Color:var(--pf-c-code-editor__controls--c-button--m-control--focus--Color)}.pf-c-code-editor__controls .pf-c-button.pf-m-control:disabled:after{border-bottom-color:var(--pf-c-code-editor__controls--c-button--m-control--disabled--after--BorderBottomColor)}.pf-c-code-editor__main{position:relative;background-color:var(--pf-c-code-editor__main--BackgroundColor);border:var(--pf-c-code-editor__main--BorderWidth) solid;border-color:var(--pf-c-code-editor__main--BorderColor)}.pf-c-code-editor__main.pf-m-drag-hover:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";background-color:var(--pf-c-code-editor__main--m-drag-hover--after--BackgroundColor);opacity:var(--pf-c-code-editor__main--m-drag-hover--after--Opacity)}.pf-c-code-editor__main.pf-m-drag-hover:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-code-editor__main--m-drag-hover--before--BorderWidth) solid var(--pf-c-code-editor__main--m-drag-hover--before--BorderColor)}.pf-c-code-editor__main .monaco-editor{background-color:var(--pf-c-code-editor__main--BackgroundColor)}.pf-c-code-editor__header+.pf-c-code-editor__main{border-top-width:0}.pf-c-code-editor__code{position:relative;padding:var(--pf-c-code-editor__code--PaddingTop) var(--pf-c-code-editor__code--PaddingRight) var(--pf-c-code-editor__code--PaddingBottom) var(--pf-c-code-editor__code--PaddingLeft)}.pf-c-code-editor__code .pf-c-code-editor__code-pre{font-family:var(--pf-c-code-editor__code-pre--FontFamily);font-size:var(--pf-c-code-editor__code-pre--FontSize);white-space:pre-wrap}.pf-c-code-editor__tab{position:relative;display:flex;align-items:center;padding:var(--pf-c-code-editor__tab--PaddingTop) var(--pf-c-code-editor__tab--PaddingRight) var(--pf-c-code-editor__tab--PaddingBottom) var(--pf-c-code-editor__tab--PaddingLeft);margin-left:auto;color:var(--pf-c-code-editor__tab--Color);background-color:var(--pf-c-code-editor__tab--BackgroundColor);border-left:var(--pf-c-code-editor__tab--BorderLeftWidth) solid var(--pf-c-code-editor__tab--BorderColor);border-bottom:var(--pf-c-code-editor__tab--BorderBottomWidth) solid var(--pf-c-code-editor__tab--BorderColor);border-right:var(--pf-c-code-editor__tab--BorderRightWidth) solid var(--pf-c-code-editor__tab--BorderColor);border-top:var(--pf-c-code-editor__tab--BorderTopWidth) solid var(--pf-c-code-editor__tab--BorderColor)}.pf-c-code-editor__tab-icon+.pf-c-code-editor__tab-text{margin-left:var(--pf-c-code-editor__tab-icon--text--MarginLeft)}.pf-c-content{--pf-c-content--MarginBottom:var(--pf-global--spacer--md);--pf-c-content--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--FontSize:var(--pf-global--FontSize--md);--pf-c-content--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--Color:var(--pf-global--Color--100);--pf-c-content--heading--FontFamily:var(--pf-global--FontFamily--heading--sans-serif);--pf-c-content--h1--MarginTop:var(--pf-global--spacer--lg);--pf-c-content--h1--MarginBottom:var(--pf-global--spacer--sm);--pf-c-content--h1--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-content--h1--FontSize:var(--pf-global--FontSize--2xl);--pf-c-content--h1--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--h2--MarginTop:var(--pf-global--spacer--lg);--pf-c-content--h2--MarginBottom:var(--pf-global--spacer--sm);--pf-c-content--h2--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--h2--FontSize:var(--pf-global--FontSize--xl);--pf-c-content--h2--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--h3--MarginTop:var(--pf-global--spacer--lg);--pf-c-content--h3--MarginBottom:var(--pf-global--spacer--sm);--pf-c-content--h3--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--h3--FontSize:var(--pf-global--FontSize--lg);--pf-c-content--h3--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--h4--MarginTop:var(--pf-global--spacer--lg);--pf-c-content--h4--MarginBottom:var(--pf-global--spacer--sm);--pf-c-content--h4--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--h4--FontSize:var(--pf-global--FontSize--md);--pf-c-content--h4--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--h5--MarginTop:var(--pf-global--spacer--lg);--pf-c-content--h5--MarginBottom:var(--pf-global--spacer--sm);--pf-c-content--h5--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--h5--FontSize:var(--pf-global--FontSize--md);--pf-c-content--h5--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--h6--MarginTop:var(--pf-global--spacer--lg);--pf-c-content--h6--MarginBottom:var(--pf-global--spacer--sm);--pf-c-content--h6--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--h6--FontSize:var(--pf-global--FontSize--md);--pf-c-content--h6--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-content--small--MarginBottom:var(--pf-global--spacer--md);--pf-c-content--small--LineHeight:var(--pf-global--LineHeight--md);--pf-c-content--small--FontSize:var(--pf-global--FontSize--sm);--pf-c-content--small--Color:var(--pf-global--Color--200);--pf-c-content--a--Color:var(--pf-global--link--Color);--pf-c-content--a--TextDecoration:var(--pf-global--link--TextDecoration);--pf-c-content--a--hover--Color:var(--pf-global--link--Color--hover);--pf-c-content--a--hover--TextDecoration:var(--pf-global--link--TextDecoration--hover);--pf-c-content--blockquote--PaddingTop:var(--pf-global--spacer--md);--pf-c-content--blockquote--PaddingRight:var(--pf-global--spacer--md);--pf-c-content--blockquote--PaddingBottom:var(--pf-global--spacer--md);--pf-c-content--blockquote--PaddingLeft:var(--pf-global--spacer--md);--pf-c-content--blockquote--Color:var(--pf-global--Color--200);--pf-c-content--blockquote--BorderLeftColor:var(--pf-global--BorderColor--100);--pf-c-content--blockquote--BorderLeftWidth:var(--pf-global--BorderWidth--lg);--pf-c-content--ol--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-content--ol--MarginLeft:var(--pf-global--spacer--lg);--pf-c-content--ol--nested--MarginTop:var(--pf-global--spacer--sm);--pf-c-content--ol--nested--MarginLeft:var(--pf-global--spacer--sm);--pf-c-content--ul--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-content--ul--MarginLeft:var(--pf-global--spacer--lg);--pf-c-content--ul--nested--MarginTop:var(--pf-global--spacer--sm);--pf-c-content--ul--nested--MarginLeft:var(--pf-global--spacer--sm);--pf-c-content--ul--ListStyle:var(--pf-global--ListStyle);--pf-c-content--li--MarginTop:var(--pf-global--spacer--sm);--pf-c-content--dl--ColumnGap:var(--pf-global--spacer--2xl);--pf-c-content--dl--RowGap:var(--pf-global--spacer--md);--pf-c-content--dt--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-content--dt--MarginTop:var(--pf-global--spacer--md);--pf-c-content--dt--sm--MarginTop:0;--pf-c-content--hr--Height:var(--pf-global--BorderWidth--sm);--pf-c-content--hr--BackgroundColor:var(--pf-global--BorderColor--100);font-size:var(--pf-c-content--FontSize);line-height:var(--pf-c-content--LineHeight);color:var(--pf-c-content--Color)}.pf-c-content a{color:var(--pf-c-content--a--Color);text-decoration:var(--pf-c-content--a--TextDecoration)}.pf-c-content a:hover{--pf-c-content--a--Color:var(--pf-c-content--a--hover--Color);--pf-c-content--a--TextDecoration:var(--pf-c-content--a--hover--TextDecoration)}.pf-c-content li+li{margin-top:var(--pf-c-content--li--MarginTop)}.pf-c-content blockquote:not(:last-child),.pf-c-content dl:not(:last-child),.pf-c-content hr:not(:last-child),.pf-c-content ol:not(:last-child),.pf-c-content p:not(:last-child),.pf-c-content pre:not(:last-child),.pf-c-content small:not(:last-child),.pf-c-content table:not(:last-child),.pf-c-content ul:not(:last-child){margin-bottom:var(--pf-c-content--MarginBottom)}.pf-c-content h1,.pf-c-content h2,.pf-c-content h3,.pf-c-content h4,.pf-c-content h5,.pf-c-content h6{margin:0;font-family:var(--pf-c-content--heading--FontFamily)}.pf-c-content h1:first-child,.pf-c-content h2:first-child,.pf-c-content h3:first-child,.pf-c-content h4:first-child,.pf-c-content h5:first-child,.pf-c-content h6:first-child{margin-top:0}.pf-c-content h1:last-child,.pf-c-content h2:last-child,.pf-c-content h3:last-child,.pf-c-content h4:last-child,.pf-c-content h5:last-child,.pf-c-content h6:last-child{margin-bottom:0}.pf-c-content ol,.pf-c-content ul{margin:0}.pf-c-content h1{margin-top:var(--pf-c-content--h1--MarginTop);margin-bottom:var(--pf-c-content--h1--MarginBottom);font-size:var(--pf-c-content--h1--FontSize);font-weight:var(--pf-c-content--h1--FontWeight);line-height:var(--pf-c-content--h1--LineHeight)}.pf-c-content h2{margin-top:var(--pf-c-content--h2--MarginTop);margin-bottom:var(--pf-c-content--h2--MarginBottom);font-size:var(--pf-c-content--h2--FontSize);font-weight:var(--pf-c-content--h2--FontWeight);line-height:var(--pf-c-content--h2--LineHeight)}.pf-c-content h3{margin-top:var(--pf-c-content--h3--MarginTop);margin-bottom:var(--pf-c-content--h3--MarginBottom);font-size:var(--pf-c-content--h3--FontSize);font-weight:var(--pf-c-content--h3--FontWeight);line-height:var(--pf-c-content--h3--LineHeight)}.pf-c-content h4{margin-top:var(--pf-c-content--h4--MarginTop);margin-bottom:var(--pf-c-content--h4--MarginBottom);font-size:var(--pf-c-content--h4--FontSize);font-weight:var(--pf-c-content--h4--FontWeight);line-height:var(--pf-c-content--h4--LineHeight)}.pf-c-content h5{margin-top:var(--pf-c-content--h5--MarginTop);margin-bottom:var(--pf-c-content--h5--MarginBottom);font-size:var(--pf-c-content--h5--FontSize);font-weight:var(--pf-c-content--h5--FontWeight);line-height:var(--pf-c-content--h5--LineHeight)}.pf-c-content h6{margin-top:var(--pf-c-content--h6--MarginTop);margin-bottom:var(--pf-c-content--h6--MarginBottom);font-size:var(--pf-c-content--h6--FontSize);font-weight:var(--pf-c-content--h6--FontWeight);line-height:var(--pf-c-content--h6--LineHeight)}.pf-c-content small{display:block;font-size:var(--pf-c-content--small--FontSize);line-height:var(--pf-c-content--small--LineHeight);color:var(--pf-c-content--small--Color)}.pf-c-content small:not(:last-child){margin-bottom:var(--pf-c-content--small--MarginBottom)}.pf-c-content blockquote{padding:var(--pf-c-content--blockquote--PaddingTop) var(--pf-c-content--blockquote--PaddingRight) var(--pf-c-content--blockquote--PaddingBottom) var(--pf-c-content--blockquote--PaddingLeft);color:var(--pf-c-content--blockquote--Color);border-left:var(--pf-c-content--blockquote--BorderLeftWidth) solid var(--pf-c-content--blockquote--BorderLeftColor)}.pf-c-content hr{height:var(--pf-c-content--hr--Height);background-color:var(--pf-c-content--hr--BackgroundColor);border:none}.pf-c-content ol{padding-left:var(--pf-c-content--ol--PaddingLeft);margin-left:var(--pf-c-content--ol--MarginLeft)}.pf-c-content ol ul{margin-top:var(--pf-c-content--ul--nested--MarginTop);--pf-c-content--ul--MarginLeft:var(--pf-c-content--ul--nested--MarginLeft)}.pf-c-content ol ol{margin-top:var(--pf-c-content--ol--nested--MarginTop);--pf-c-content--ol--MarginLeft:var(--pf-c-content--ol--nested--MarginLeft)}.pf-c-content ul{padding-left:var(--pf-c-content--ul--PaddingLeft);margin-left:var(--pf-c-content--ul--MarginLeft);list-style:var(--pf-c-content--ul--ListStyle)}.pf-c-content ul ul{margin-top:var(--pf-c-content--ul--nested--MarginTop);--pf-c-content--ul--MarginLeft:var(--pf-c-content--ul--nested--MarginLeft)}.pf-c-content ul ol{margin-top:var(--pf-c-content--ol--nested--MarginTop);--pf-c-content--ol--MarginLeft:var(--pf-c-content--ol--nested--MarginLeft)}.pf-c-content dl{display:grid;grid-template-columns:1fr}@media screen and (min-width:576px){.pf-c-content dl{grid-template:auto/auto 1fr;grid-column-gap:var(--pf-c-content--dl--ColumnGap);grid-row-gap:var(--pf-c-content--dl--RowGap)}}.pf-c-content dt{font-weight:var(--pf-c-content--dt--FontWeight)}.pf-c-content dt:not(:first-child){margin-top:var(--pf-c-content--dt--MarginTop)}@media screen and (min-width:576px){.pf-c-content dt:not(:first-child){--pf-c-content--dt--MarginTop:var(--pf-c-content--dt--sm--MarginTop)}}@media screen and (min-width:576px){.pf-c-content dt{grid-column:1}}@media screen and (min-width:576px){.pf-c-content dd{grid-column:2}}.pf-m-overpass-font .pf-c-content{--pf-c-content--h2--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-content--h4--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-content--h5--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-content--h6--FontWeight:var(--pf-global--FontWeight--semi-bold)}.pf-m-overpass-font .pf-c-content blockquote{font-weight:var(--pf-global--FontWeight--light)}.pf-c-context-selector{--pf-c-context-selector--Width:15.625rem;--pf-c-context-selector__toggle--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-context-selector__toggle--PaddingRight:var(--pf-global--spacer--sm);--pf-c-context-selector__toggle--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-context-selector__toggle--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-context-selector__toggle--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-context-selector__toggle--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-context-selector__toggle--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-context-selector__toggle--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-context-selector__toggle--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-context-selector__toggle--Color:var(--pf-global--Color--100);--pf-c-context-selector__toggle--hover--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-context-selector__toggle--active--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-context-selector__toggle--active--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-context-selector__toggle--expanded--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-context-selector__toggle--expanded--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-context-selector__toggle-text--FontSize:var(--pf-global--FontSize--md);--pf-c-context-selector__toggle-text--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-context-selector__toggle-text--LineHeight:var(--pf-global--LineHeight--md);--pf-c-context-selector__toggle-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-context-selector__toggle-icon--MarginLeft:var(--pf-global--spacer--md);--pf-c-context-selector__menu--Top:calc(100% + var(--pf-global--spacer--xs));--pf-c-context-selector__menu--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-context-selector__menu--PaddingTop:var(--pf-global--spacer--sm);--pf-c-context-selector__menu--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-context-selector__menu--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-context-selector__menu-search--PaddingTop:var(--pf-global--spacer--sm);--pf-c-context-selector__menu-search--PaddingRight:var(--pf-global--spacer--md);--pf-c-context-selector__menu-search--PaddingBottom:var(--pf-global--spacer--md);--pf-c-context-selector__menu-search--PaddingLeft:var(--pf-global--spacer--md);--pf-c-context-selector__menu-search--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-context-selector__menu-search--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-context-selector__menu-footer--BoxShadow:var(--pf-global--BoxShadow--sm-top);--pf-c-context-selector__menu-footer--PaddingTop:var(--pf-global--spacer--md);--pf-c-context-selector__menu-footer--PaddingRight:var(--pf-global--spacer--md);--pf-c-context-selector__menu-footer--PaddingBottom:var(--pf-global--spacer--md);--pf-c-context-selector__menu-footer--PaddingLeft:var(--pf-global--spacer--md);--pf-c-context-selector__menu-list--MaxHeight:12.5rem;--pf-c-context-selector__menu-list-item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-context-selector__menu-list-item--PaddingRight:var(--pf-global--spacer--lg);--pf-c-context-selector__menu-list-item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-context-selector__menu-list-item--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-context-selector__menu-list-item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-context-selector__menu-list-item--disabled--Color:var(--pf-global--Color--dark-200);position:relative;display:inline-block;width:var(--pf-c-context-selector--Width);max-width:100%}.pf-c-context-selector__toggle{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;padding:var(--pf-c-context-selector__toggle--PaddingTop) var(--pf-c-context-selector__toggle--PaddingRight) var(--pf-c-context-selector__toggle--PaddingBottom) var(--pf-c-context-selector__toggle--PaddingLeft);color:var(--pf-c-context-selector__toggle--Color);white-space:nowrap;cursor:pointer;border:none}.pf-c-context-selector__toggle:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-context-selector__toggle--BorderWidth) solid;border-color:var(--pf-c-context-selector__toggle--BorderTopColor) var(--pf-c-context-selector__toggle--BorderRightColor) var(--pf-c-context-selector__toggle--BorderBottomColor) var(--pf-c-context-selector__toggle--BorderLeftColor)}.pf-c-context-selector__toggle:hover:before{--pf-c-context-selector__toggle--BorderBottomColor:var(--pf-c-context-selector__toggle--hover--BorderBottomColor)}.pf-c-context-selector__toggle.pf-m-active:before,.pf-c-context-selector__toggle:active:before,.pf-c-context-selector__toggle:focus-within:before{--pf-c-context-selector__toggle--BorderBottomColor:var(--pf-c-context-selector__toggle--active--BorderBottomColor);border-bottom-width:var(--pf-c-context-selector__toggle--active--BorderBottomWidth)}.pf-m-expanded>.pf-c-context-selector__toggle:before{--pf-c-context-selector__toggle--BorderBottomColor:var(--pf-c-context-selector__toggle--expanded--BorderBottomColor);border-bottom-width:var(--pf-c-context-selector__toggle--expanded--BorderBottomWidth)}.pf-c-context-selector__toggle .pf-c-context-selector__toggle-icon{margin-right:var(--pf-c-context-selector__toggle-icon--MarginRight);margin-left:var(--pf-c-context-selector__toggle-icon--MarginLeft)}.pf-c-context-selector__toggle .pf-c-context-selector__toggle-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--pf-c-context-selector__toggle-text--FontSize);font-weight:var(--pf-c-context-selector__toggle-text--FontWeight);line-height:var(--pf-c-context-selector__toggle-text--LineHeight)}.pf-c-context-selector__menu{color:var(--pf-global--Color--100);position:absolute;top:var(--pf-c-context-selector__menu--Top);z-index:var(--pf-c-context-selector__menu--ZIndex);min-width:100%;padding-top:var(--pf-c-context-selector__menu--PaddingTop);background-color:var(--pf-c-context-selector__menu--BackgroundColor);background-clip:padding-box;box-shadow:var(--pf-c-context-selector__menu--BoxShadow)}.pf-c-context-selector__menu-search{position:relative;padding:var(--pf-c-context-selector__menu-search--PaddingTop) var(--pf-c-context-selector__menu-search--PaddingRight) var(--pf-c-context-selector__menu-search--PaddingBottom) var(--pf-c-context-selector__menu-search--PaddingLeft);border-bottom:var(--pf-c-context-selector__menu-search--BorderBottomWidth) solid var(--pf-c-context-selector__menu-search--BorderBottomColor)}.pf-c-context-selector__menu-footer{padding:var(--pf-c-context-selector__menu-footer--PaddingTop) var(--pf-c-context-selector__menu-footer--PaddingRight) var(--pf-c-context-selector__menu-footer--PaddingBottom) var(--pf-c-context-selector__menu-footer--PaddingLeft);text-align:right;box-shadow:var(--pf-c-context-selector__menu-footer--BoxShadow)}.pf-c-context-selector__menu-list{max-height:var(--pf-c-context-selector__menu-list--MaxHeight);overflow-y:scroll}.pf-c-context-selector__menu-list-item{display:flex;align-items:center;width:100%;padding:var(--pf-c-context-selector__menu-list-item--PaddingTop) var(--pf-c-context-selector__menu-list-item--PaddingRight) var(--pf-c-context-selector__menu-list-item--PaddingBottom) var(--pf-c-context-selector__menu-list-item--PaddingLeft);white-space:nowrap;border:none}.pf-c-context-selector__menu-list-item:focus,.pf-c-context-selector__menu-list-item:hover{text-decoration:none;background-color:var(--pf-c-context-selector__menu-list-item--hover--BackgroundColor)}.pf-c-context-selector__menu-list-item:disabled{color:var(--pf-c-context-selector__menu-list-item--disabled--Color);pointer-events:none}@media screen and (min-width:768px){.pf-c-data-list:not([class*=pf-m-grid]){--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:768px) and (min-width:1200px){.pf-c-data-list:not([class*=pf-m-grid]){--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:768px){.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list:not([class*=pf-m-grid]) .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}@media screen and (min-width:0){.pf-c-data-list.pf-m-grid-none{--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:0) and (min-width:1200px){.pf-c-data-list.pf-m-grid-none{--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:0){.pf-c-data-list.pf-m-grid-none .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list.pf-m-grid-none .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}@media screen and (min-width:576px){.pf-c-data-list.pf-m-grid-sm{--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:576px) and (min-width:1200px){.pf-c-data-list.pf-m-grid-sm{--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:576px){.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list.pf-m-grid-sm .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}@media screen and (min-width:768px){.pf-c-data-list.pf-m-grid-md{--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:768px) and (min-width:1200px){.pf-c-data-list.pf-m-grid-md{--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:768px){.pf-c-data-list.pf-m-grid-md .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list.pf-m-grid-md .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}@media screen and (min-width:992px){.pf-c-data-list.pf-m-grid-lg{--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:992px) and (min-width:1200px){.pf-c-data-list.pf-m-grid-lg{--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:992px){.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list.pf-m-grid-lg .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}@media screen and (min-width:1200px){.pf-c-data-list.pf-m-grid-xl{--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:1200px) and (min-width:1200px){.pf-c-data-list.pf-m-grid-xl{--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:1200px){.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list.pf-m-grid-xl .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}@media screen and (min-width:1450px){.pf-c-data-list.pf-m-grid-2xl{--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list__cell--cell--md--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list__cell--md--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list__item-control--md--MarginRight);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list__item-action--md--MarginLeft);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-c-data-list__expandable-content-body--md--PaddingTop);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);--pf-c-data-list--m-compact__cell-cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop)}}@media screen and (min-width:1450px) and (min-width:1200px){.pf-c-data-list.pf-m-grid-2xl{--pf-c-data-list__item-row--PaddingRight:var(--pf-c-data-list__item-row--xl--PaddingRight);--pf-c-data-list__item-row--PaddingLeft:var(--pf-c-data-list__item-row--xl--PaddingLeft);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft)}}@media screen and (min-width:1450px){.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__item-content{display:flex;flex-wrap:wrap;flex-grow:1;padding-bottom:var(--pf-c-data-list__item-content--md--PaddingBottom)}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon){margin-right:var(--pf-c-data-list__cell--MarginRight)}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell+.pf-c-data-list__cell{flex:1;order:0}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-align-right{margin-left:auto}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-no-fill{flex-grow:0}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-2{flex-grow:2}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-3{flex-grow:3}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-4{flex-grow:4}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-5{flex-grow:5}.pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__expandable-content{max-height:none;overflow-y:visible}}.pf-c-data-list{--pf-c-data-list--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-data-list--BorderTopWidth:var(--pf-global--spacer--sm);--pf-c-data-list--sm--BorderTopWidth:var(--pf-global--BorderWidth--sm);--pf-c-data-list--sm--BorderTopColor:var(--pf-global--BorderColor--100);--pf-c-data-list__item--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-data-list__item--m-selected--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-data-list__item--m-expanded--before--BackgroundColor:var(--pf-global--active-color--100);--pf-c-data-list__item--m-selected--before--BackgroundColor:var(--pf-global--active-color--100);--pf-c-data-list__item--m-selected--BoxShadow:var(--pf-global--BoxShadow--sm-top),var(--pf-global--BoxShadow--sm-bottom);--pf-c-data-list__item--m-selectable--OutlineOffset:calc(-1*var(--pf-global--spacer--xs));--pf-c-data-list__item--m-selectable--hover--ZIndex:calc(var(--pf-c-data-list__item--m-selected--ZIndex) + 1);--pf-c-data-list__item--m-selectable--hover--BoxShadow:var(--pf-global--BoxShadow--sm-top),var(--pf-global--BoxShadow--sm-bottom);--pf-c-data-list__item--m-selectable--focus--BoxShadow:var(--pf-global--BoxShadow--sm-top),var(--pf-global--BoxShadow--sm-bottom);--pf-c-data-list__item--m-selectable--active--BoxShadow:var(--pf-global--BoxShadow--sm-top),var(--pf-global--BoxShadow--sm-bottom);--pf-c-data-list__item--m-expanded--m-selectable--before--BackgroundColor:var(--pf-global--active-color--300);--pf-c-data-list__item--BorderBottomColor:var(--pf-global--BorderColor--300);--pf-c-data-list__item--BorderBottomWidth:0.5rem;--pf-c-data-list__item--m-selectable--hover--item--BorderTopColor:var(--pf-c-data-list__item--BorderBottomColor);--pf-c-data-list__item--m-selectable--hover--item--BorderTopWidth:var(--pf-c-data-list__item--BorderBottomWidth);--pf-c-data-list__item--sm--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-data-list__item--sm--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-data-list__item--before--BackgroundColor:transparent;--pf-c-data-list__item--before--Width:var(--pf-global--BorderWidth--lg);--pf-c-data-list__item--before--Transition:var(--pf-global--Transition);--pf-c-data-list__item--before--Top:0;--pf-c-data-list__item--before--sm--Top:calc(var(--pf-c-data-list__item--BorderBottomWidth)*-1);--pf-c-data-list__item-row--PaddingRight:var(--pf-global--spacer--md);--pf-c-data-list__item-row--PaddingLeft:var(--pf-global--spacer--md);--pf-c-data-list__item-row--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-data-list__item-row--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-data-list__item-content--md--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-data-list__cell--PaddingTop:var(--pf-global--spacer--lg);--pf-c-data-list__cell--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-data-list__cell--MarginRight:var(--pf-global--spacer--xl);--pf-c-data-list__cell--md--PaddingBottom:0;--pf-c-data-list__cell--m-icon--MarginRight:var(--pf-global--spacer--md);--pf-c-data-list__cell--cell--PaddingTop:0;--pf-c-data-list__cell--cell--md--PaddingTop:var(--pf-global--spacer--lg);--pf-c-data-list__cell--m-icon--cell--PaddingTop:var(--pf-global--spacer--lg);--pf-c-data-list--cell--MinWidth:initial;--pf-c-data-list--cell--Overflow:visible;--pf-c-data-list--cell--TextOverflow:clip;--pf-c-data-list--cell--WhiteSpace:normal;--pf-c-data-list--cell--WordBreak:normal;--pf-c-data-list--cell--m-truncate--MinWidth:5ch;--pf-c-data-list__toggle--MarginLeft:calc(var(--pf-global--spacer--sm)*-1);--pf-c-data-list__toggle--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-data-list__toggle-icon--Transition:.2s ease-in 0s;--pf-c-data-list__item--m-expanded__toggle-icon--Rotate:90deg;--pf-c-data-list__item-draggable-button--PaddingLeft:var(--pf-global--spacer--md);--pf-c-data-list__item-draggable-button--PaddingRight:var(--pf-global--spacer--md);--pf-c-data-list__item-draggable-button--MarginTop:calc(var(--pf-global--spacer--sm)*-1);--pf-c-data-list__item-draggable-button--MarginLeft:calc(var(--pf-global--spacer--md)*-1);--pf-c-data-list__item-draggable-button-icon--Color:var(--pf-global--icon--Color--light);--pf-c-data-list__item-draggable-button--m-disabled__draggable-icon--Color:var(--pf-global--disabled-color--200);--pf-c-data-list__item-draggable-button--hover__draggable-icon--Color:var(--pf-global--icon--Color--dark);--pf-c-data-list__item-draggable-button--focus__draggable-icon--Color:var(--pf-global--icon--Color--dark);--pf-c-data-list__item--m-ghost-row--after--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-data-list__item--m-ghost-row--after--Opacity:.6;--pf-c-data-list__item-control--PaddingTop:var(--pf-global--spacer--lg);--pf-c-data-list__item-control--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-data-list__item-control--MarginRight:var(--pf-global--spacer--md);--pf-c-data-list__item-control--md--MarginRight:var(--pf-global--spacer--xl);--pf-c-data-list__item-control--not-last-child--MarginRight:var(--pf-global--spacer--md);--pf-c-data-list__item-action--Display:flex;--pf-c-data-list__item-action--PaddingTop:var(--pf-global--spacer--lg);--pf-c-data-list__item-action--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-data-list__item-action--MarginLeft:var(--pf-global--spacer--md);--pf-c-data-list__item-action--md--MarginLeft:var(--pf-global--spacer--xl);--pf-c-data-list__item-action--not-last-child--MarginRight:var(--pf-global--spacer--md);--pf-c-data-list__action--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-data-list__expandable-content--BorderTopWidth:var(--pf-global--BorderWidth--sm);--pf-c-data-list__expandable-content--BorderTopColor:var(--pf-global--BorderColor--100);--pf-c-data-list__expandable-content--MarginRight:calc(var(--pf-c-data-list__expandable-content-body--PaddingRight)*-1);--pf-c-data-list__expandable-content--MarginLeft:calc(var(--pf-c-data-list__expandable-content-body--PaddingLeft)*-1);--pf-c-data-list__expandable-content--MaxHeight:37.5rem;--pf-c-data-list__expandable-content--before--Top:calc(var(--pf-c-data-list__item--BorderBottomWidth)*-1);--pf-c-data-list__expandable-content-body--PaddingTop:var(--pf-global--spacer--md);--pf-c-data-list__expandable-content-body--PaddingRight:var(--pf-global--spacer--md);--pf-c-data-list__expandable-content-body--PaddingBottom:var(--pf-global--spacer--md);--pf-c-data-list__expandable-content-body--PaddingLeft:var(--pf-global--spacer--md);--pf-c-data-list__expandable-content-body--md--PaddingTop:var(--pf-global--spacer--lg);--pf-c-data-list__expandable-content-body--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-data-list__expandable-content-body--md--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-data-list__expandable-content-body--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-data-list--m-compact--FontSize:var(--pf-global--FontSize--sm);--pf-c-data-list--m-compact__check--FontSize:var(--pf-global--FontSize--md);--pf-c-data-list--m-compact__cell--PaddingTop:var(--pf-global--spacer--sm);--pf-c-data-list--m-compact__cell--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-data-list--m-compact__cell--md--PaddingBottom:0;--pf-c-data-list--m-compact__cell-cell--PaddingTop:0;--pf-c-data-list--m-compact__cell-cell--md--PaddingTop:var(--pf-global--spacer--sm);--pf-c-data-list--m-compact__cell--cell--MarginRight:var(--pf-global--spacer--md);--pf-c-data-list--m-compact__item-control--PaddingTop:var(--pf-global--spacer--sm);--pf-c-data-list--m-compact__item-control--PaddingBottom:0;--pf-c-data-list--m-compact__item-control--MarginRight:var(--pf-global--spacer--md);--pf-c-data-list--m-compact__item-action--PaddingTop:var(--pf-global--spacer--sm);--pf-c-data-list--m-compact__item-action--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-data-list--m-compact__item-action--MarginLeft:var(--pf-global--spacer--md);--pf-c-data-list--m-compact__item-content--PaddingBottom:var(--pf-global--spacer--sm);color:var(--pf-global--Color--100);overflow-wrap:break-word;list-style-type:disc;border-top:var(--pf-c-data-list--BorderTopWidth) solid var(--pf-c-data-list--BorderTopColor)}@media screen and (min-width:576px){.pf-c-data-list{--pf-c-data-list--BorderTopColor:var(--pf-c-data-list--sm--BorderTopColor);--pf-c-data-list--BorderTopWidth:var(--pf-c-data-list--sm--BorderTopWidth);--pf-c-data-list__item--BorderBottomWidth:var(--pf-c-data-list__item--sm--BorderBottomWidth);--pf-c-data-list__item--BorderBottomColor:var(--pf-c-data-list__item--sm--BorderBottomColor)}}@media (min-width:576px){.pf-c-data-list{--pf-c-data-list__item--before--Top:var(--pf-c-data-list__item--before--sm--Top)}}.pf-c-data-list.pf-m-compact{font-size:var(--pf-c-data-list--m-compact--FontSize);--pf-c-data-list__item-action--MarginLeft:var(--pf-c-data-list--m-compact__item-action--MarginLeft);--pf-c-data-list__item-action--PaddingTop:var(--pf-c-data-list--m-compact__item-action--PaddingTop);--pf-c-data-list__item-action--PaddingBottom:var(--pf-c-data-list--m-compact__item-action--PaddingBottom);--pf-c-data-list__item-control--MarginRight:var(--pf-c-data-list--m-compact__item-control--MarginRight);--pf-c-data-list__item-control--PaddingTop:var(--pf-c-data-list--m-compact__item-control--PaddingTop);--pf-c-data-list__item-control--PaddingBottom:var(--pf-c-data-list--m-compact__item-control--PaddingBottom);--pf-c-data-list__item-content--md--PaddingBottom:var(--pf-c-data-list--m-compact__item-content--PaddingBottom)}.pf-c-data-list.pf-m-compact .pf-c-data-list__cell{--pf-c-data-list__cell--PaddingTop:var(--pf-c-data-list--m-compact__cell--PaddingTop);--pf-c-data-list__cell--PaddingBottom:var(--pf-c-data-list--m-compact__cell--PaddingBottom);--pf-c-data-list__cell--MarginRight:var(--pf-c-data-list--m-compact__cell--cell--MarginRight);--pf-c-data-list__cell--cell--PaddingTop:var(--pf-c-data-list--m-compact__cell-cell--PaddingTop)}.pf-c-data-list.pf-m-compact .pf-c-data-list__check{font-size:var(--pf-c-data-list--m-compact__check--FontSize)}.pf-c-data-list.pf-m-drag-over{overflow-anchor:none}.pf-c-data-list.pf-m-truncate,.pf-c-data-list__cell.pf-m-truncate,.pf-c-data-list__item-row.pf-m-truncate,.pf-c-data-list__text.pf-m-truncate{--pf-c-data-list--cell--MinWidth:var(--pf-c-data-list--cell--m-truncate--MinWidth);--pf-c-data-list--cell--Overflow:hidden;--pf-c-data-list--cell--TextOverflow:ellipsis;--pf-c-data-list--cell--WhiteSpace:nowrap}.pf-c-data-list.pf-m-break-word,.pf-c-data-list__cell.pf-m-break-word,.pf-c-data-list__item-row.pf-m-break-word,.pf-c-data-list__text.pf-m-break-word{--pf-c-data-list--cell--WordBreak:break-word}.pf-c-data-list.pf-m-nowrap,.pf-c-data-list__cell.pf-m-nowrap,.pf-c-data-list__item-row.pf-m-nowrap,.pf-c-data-list__text.pf-m-nowrap{--pf-c-data-list--cell--WhiteSpace:nowrap}.pf-c-data-list__item{position:relative;display:flex;flex-direction:column;background-color:var(--pf-c-data-list__item--BackgroundColor);border-bottom:var(--pf-c-data-list__item--BorderBottomWidth) solid var(--pf-c-data-list__item--BorderBottomColor)}.pf-c-data-list__item:before{position:absolute;top:var(--pf-c-data-list__item--before--Top);bottom:0;left:0;width:var(--pf-c-data-list__item--before--Width);content:"";background-color:var(--pf-c-data-list__item--before--BackgroundColor);transition:var(--pf-c-data-list__item--before--Transition)}.pf-c-data-list__item.pf-m-selectable{cursor:pointer;outline-offset:var(--pf-c-data-list__item--m-selectable--OutlineOffset)}.pf-c-data-list__item.pf-m-selectable:focus,.pf-c-data-list__item.pf-m-selectable:hover{position:relative;z-index:var(--pf-c-data-list__item--m-selectable--hover--ZIndex)}.pf-c-data-list__item.pf-m-selectable:focus:not(.pf-m-selected):not(:last-child),.pf-c-data-list__item.pf-m-selectable:hover:not(.pf-m-selected):not(:last-child){--pf-c-data-list__item--BorderBottomWidth:0}.pf-c-data-list__item.pf-m-selectable:focus:not(.pf-m-selected):not(:last-child)+.pf-c-data-list__item,.pf-c-data-list__item.pf-m-selectable:hover:not(.pf-m-selected):not(:last-child)+.pf-c-data-list__item{border-top:var(--pf-c-data-list__item--m-selectable--hover--item--BorderTopWidth) solid var(--pf-c-data-list__item--m-selectable--hover--item--BorderTopColor)}.pf-c-data-list__item.pf-m-selectable:hover{box-shadow:var(--pf-c-data-list__item--m-selectable--hover--BoxShadow)}.pf-c-data-list__item.pf-m-selectable:focus{box-shadow:var(--pf-c-data-list__item--m-selectable--focus--BoxShadow)}.pf-c-data-list__item.pf-m-selectable:active{box-shadow:var(--pf-c-data-list__item--m-selectable--active--BoxShadow)}.pf-c-data-list__item.pf-m-selected{--pf-c-data-list__item--before--BackgroundColor:var(--pf-c-data-list__item--m-selected--before--BackgroundColor);position:relative;z-index:var(--pf-c-data-list__item--m-selected--ZIndex);box-shadow:var(--pf-c-data-list__item--m-selected--BoxShadow)}.pf-c-data-list__item.pf-m-ghost-row:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";background-color:var(--pf-c-data-list__item--m-ghost-row--after--BackgroundColor);opacity:var(--pf-c-data-list__item--m-ghost-row--after--Opacity)}.pf-c-data-list__item.pf-m-expanded{--pf-c-data-list__item--before--BackgroundColor:var(--pf-c-data-list__item--m-expanded--before--BackgroundColor)}.pf-c-data-list__item.pf-m-expanded.pf-m-selectable:not(.pf-m-selected){--pf-c-data-list__item--before--BackgroundColor:var(--pf-c-data-list__item--m-expanded--m-selectable--before--BackgroundColor)}.pf-c-data-list__item-row{display:flex;flex-wrap:nowrap;padding-right:var(--pf-c-data-list__item-row--PaddingRight);padding-left:var(--pf-c-data-list__item-row--PaddingLeft)}.pf-c-data-list__item-control{display:flex;flex-wrap:nowrap;padding-top:var(--pf-c-data-list__item-control--PaddingTop);padding-bottom:var(--pf-c-data-list__item-control--PaddingBottom);margin-right:var(--pf-c-data-list__item-control--MarginRight)}.pf-c-data-list__item-control>:not(:last-child){margin-right:var(--pf-c-data-list__item-control--not-last-child--MarginRight)}.pf-c-data-list__item-draggable-button{padding-right:var(--pf-c-data-list__item-draggable-button--PaddingRight);padding-left:var(--pf-c-data-list__item-draggable-button--PaddingLeft);margin-top:var(--pf-c-data-list__item-draggable-button--MarginTop);margin-left:var(--pf-c-data-list__item-draggable-button--MarginLeft);border:0}.pf-c-data-list__item-draggable-button:hover{--pf-c-data-list__item-draggable-button-icon--Color:var(--pf-c-data-list__item-draggable-button--hover__draggable-icon--Color);cursor:grab}.pf-c-data-list__item-draggable-button:focus{--pf-c-data-list__item-draggable-button-icon--Color:var(--pf-c-data-list__item-draggable-button--focus__draggable-icon--Color)}.pf-c-data-list__item-draggable-button:active{cursor:grabbing}.pf-c-data-list__item-draggable-button.pf-m-disabled{--pf-c-data-list__item-draggable-button-icon--Color:var(--pf-c-data-list__item-draggable-button--m-disabled__draggable-icon--Color);pointer-events:none;cursor:none}.pf-c-data-list__item-draggable-button .pf-c-data-list__item-draggable-icon{color:var(--pf-c-data-list__item-draggable-button-icon--Color)}.pf-c-data-list__item-action{--pf-hidden-visible--visible--Display:var(--pf-c-data-list__item-action--Display);align-items:flex-start;align-content:flex-start;padding-top:var(--pf-c-data-list__item-action--PaddingTop);padding-bottom:var(--pf-c-data-list__item-action--PaddingBottom);margin-left:var(--pf-c-data-list__item-action--MarginLeft)}.pf-c-data-list__item-action>:not(:last-child){margin-right:var(--pf-c-data-list__item-action--not-last-child--MarginRight)}.pf-c-data-list__item-action .pf-c-data-list__action{margin-top:var(--pf-c-data-list__action--MarginTop)}.pf-c-data-list__toggle{margin-top:var(--pf-c-data-list__toggle--MarginTop);margin-left:var(--pf-c-data-list__toggle--MarginLeft)}.pf-c-data-list__toggle-icon{pointer-events:none;transition:var(--pf-c-data-list__toggle-icon--Transition)}.pf-c-data-list__item.pf-m-expanded .pf-c-data-list__toggle-icon{transform:rotate(var(--pf-c-data-list__item--m-expanded__toggle-icon--Rotate))}.pf-c-data-list__item-content{display:grid;width:100%;grid-template-columns:auto 1fr}.pf-c-data-list__cell{flex:1;grid-column:1/-1;padding-top:var(--pf-c-data-list__cell--PaddingTop);padding-bottom:var(--pf-c-data-list__cell--PaddingBottom)}.pf-c-data-list__cell+.pf-c-data-list__cell{flex:1 0 100%;order:1;padding-top:var(--pf-c-data-list__cell--cell--PaddingTop)}.pf-c-data-list__cell.pf-m-icon{flex-grow:0;margin-right:var(--pf-c-data-list__cell--m-icon--MarginRight);grid-column:1/2}.pf-c-data-list__cell.pf-m-icon+.pf-c-data-list__cell{grid-column:2/3;padding-top:var(--pf-c-data-list__cell--m-icon--cell--PaddingTop)}.pf-c-data-list__cell.pf-m-align-right{margin-left:0}.pf-c-data-list__text{display:inline-block}.pf-c-data-list__cell,.pf-c-data-list__text{min-width:var(--pf-c-data-list--cell--MinWidth);max-width:100%;overflow:var(--pf-c-data-list--cell--Overflow);text-overflow:var(--pf-c-data-list--cell--TextOverflow);word-break:var(--pf-c-data-list--cell--WordBreak);white-space:var(--pf-c-data-list--cell--WhiteSpace)}.pf-c-data-list__expandable-content{max-height:var(--pf-c-data-list__expandable-content--MaxHeight);overflow-y:auto;border-top:var(--pf-c-data-list__expandable-content--BorderTopWidth) solid var(--pf-c-data-list__expandable-content--BorderTopColor)}.pf-c-data-list__expandable-content .pf-c-data-list__expandable-content-body{padding:var(--pf-c-data-list__expandable-content-body--PaddingTop) var(--pf-c-data-list__expandable-content-body--PaddingRight) var(--pf-c-data-list__expandable-content-body--PaddingBottom) var(--pf-c-data-list__expandable-content-body--PaddingLeft)}.pf-c-data-list__expandable-content .pf-c-data-list__expandable-content-body.pf-m-no-padding{padding:0}.pf-c-description-list{--pf-c-description-list--RowGap:var(--pf-global--gutter--md);--pf-c-description-list--ColumnGap:var(--pf-global--spacer--lg);--pf-c-description-list--GridTemplateColumns--count:1;--pf-c-description-list--GridTemplateColumns--width:1fr;--pf-c-description-list--GridTemplateColumns:repeat(var(--pf-c-description-list--GridTemplateColumns--count),var(--pf-c-description-list--GridTemplateColumns--width));--pf-c-description-list__group--RowGap:var(--pf-global--spacer--sm);--pf-c-description-list__group--ColumnGap:var(--pf-global--spacer--md);--pf-c-description-list__group--GridTemplateColumns:auto;--pf-c-description-list__group--GridColumn:auto;--pf-c-description-list__term--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-description-list__term--FontSize:var(--pf-global--FontSize--sm);--pf-c-description-list--m-horizontal__term--width:12ch;--pf-c-description-list--m-horizontal__description--width:minmax(10ch,auto);--pf-c-description-list--m-horizontal__group--GridTemplateColumns:var(--pf-c-description-list__term--width) var(--pf-c-description-list--m-horizontal__description--width);--pf-c-description-list--m-1-col--GridTemplateColumns--count:1;--pf-c-description-list--m-auto-fit--GridTemplateColumns--min:15.625rem;--pf-c-description-list--m-auto-fit--GridTemplateColumns--minmax--min:var(--pf-c-description-list--m-auto-fit--GridTemplateColumns--min);display:grid;align-items:baseline;row-gap:var(--pf-c-description-list--RowGap);column-gap:var(--pf-c-description-list--ColumnGap);grid-template-columns:var(--pf-c-description-list--GridTemplateColumns)}@media screen and (min-width:768px){.pf-c-description-list{--pf-c-description-list--m-2-col--GridTemplateColumns--count:2;--pf-c-description-list--m-3-col--GridTemplateColumns--count:3}}.pf-c-description-list.pf-m-horizontal{--pf-c-description-list__group--GridTemplateColumns:var(--pf-c-description-list--m-horizontal__group--GridTemplateColumns);--pf-c-description-list__term--width:var(--pf-c-description-list--m-horizontal__term--width)}@media (min-width:768px){.pf-c-description-list.pf-m-horizontal{--pf-c-description-list__term--width:var(--pf-c-description-list--m-horizontal__term--width-on-md,var(--pf-c-description-list--m-horizontal__term--width))}}@media (min-width:992px){.pf-c-description-list.pf-m-horizontal{--pf-c-description-list__term--width:var(--pf-c-description-list--m-horizontal__term--width-on-lg,var(--pf-c-description-list--m-horizontal__term--width-on-md,var(--pf-c-description-list--m-horizontal__term--width)))}}@media (min-width:1200px){.pf-c-description-list.pf-m-horizontal{--pf-c-description-list__term--width:var(--pf-c-description-list--m-horizontal__term--width-on-xl,var(--pf-c-description-list--m-horizontal__term--width-on-lg,var(--pf-c-description-list--m-horizontal__term--width-on-md,var(--pf-c-description-list--m-horizontal__term--width))))}}@media (min-width:1450px){.pf-c-description-list.pf-m-horizontal{--pf-c-description-list__term--width:var(--pf-c-description-list--m-horizontal__term--width-on-2xl,var(--pf-c-description-list--m-horizontal__term--width-on-xl,var(--pf-c-description-list--m-horizontal__term--width-on-lg,var(--pf-c-description-list--m-horizontal__term--width-on-md,var(--pf-c-description-list--m-horizontal__term--width)))))}}.pf-c-description-list.pf-m-inline-grid{display:inline-grid}.pf-c-description-list.pf-m-auto-column-widths{--pf-c-description-list--GridTemplateColumns--width:minmax(8ch,max-content)}.pf-c-description-list.pf-m-auto-fit{grid-template-columns:repeat(auto-fit,minmax(var(--pf-c-description-list--m-auto-fit--GridTemplateColumns--minmax--min),1fr));--pf-c-description-list--GridTemplateColumns--minmax--min:var(--pf-c-description-list--GridTemplateColumns--min)}@media (min-width:768px){.pf-c-description-list.pf-m-auto-fit{--pf-c-description-list--GridTemplateColumns--minmax--min:var(--pf-c-description-list--GridTemplateColumns--min-on-md,var(--pf-c-description-list--GridTemplateColumns--min))}}@media (min-width:992px){.pf-c-description-list.pf-m-auto-fit{--pf-c-description-list--GridTemplateColumns--minmax--min:var(--pf-c-description-list--GridTemplateColumns--min-on-lg,var(--pf-c-description-list--GridTemplateColumns--min-on-md,var(--pf-c-description-list--GridTemplateColumns--min)))}}@media (min-width:1200px){.pf-c-description-list.pf-m-auto-fit{--pf-c-description-list--GridTemplateColumns--minmax--min:var(--pf-c-description-list--GridTemplateColumns--min-on-xl,var(--pf-c-description-list--GridTemplateColumns--min-on-lg,var(--pf-c-description-list--GridTemplateColumns--min-on-md,var(--pf-c-description-list--GridTemplateColumns--min))))}}@media (min-width:1450px){.pf-c-description-list.pf-m-auto-fit{--pf-c-description-list--GridTemplateColumns--minmax--min:var(--pf-c-description-list--GridTemplateColumns--min-on-2xl,var(--pf-c-description-list--GridTemplateColumns--min-on-xl,var(--pf-c-description-list--GridTemplateColumns--min-on-lg,var(--pf-c-description-list--GridTemplateColumns--min-on-md,var(--pf-c-description-list--GridTemplateColumns--min)))))}}.pf-c-description-list__group{display:grid;grid-column:var(--pf-c-description-list__group--GridColumn);row-gap:var(--pf-c-description-list__group--RowGap);column-gap:var(--pf-c-description-list__group--ColumnGap);grid-template-columns:var(--pf-c-description-list__group--GridTemplateColumns);align-items:baseline}.pf-c-description-list__description,.pf-c-description-list__term{text-align:left}.pf-c-description-list__term{font-size:var(--pf-c-description-list__term--FontSize);font-weight:var(--pf-c-description-list__term--FontWeight)}.pf-c-description-list__term .pf-c-description-list__text{display:inline}.pf-c-description-list.pf-m-1-col{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-1-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-2-col{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-2-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-3-col{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-3-col--GridTemplateColumns--count)}@media (min-width:768px){.pf-c-description-list.pf-m-1-col-on-md{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-1-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-2-col-on-md{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-2-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-3-col-on-md{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-3-col--GridTemplateColumns--count)}}@media (min-width:992px){.pf-c-description-list.pf-m-1-col-on-lg{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-1-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-2-col-on-lg{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-2-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-3-col-on-lg{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-3-col--GridTemplateColumns--count)}}@media (min-width:1200px){.pf-c-description-list.pf-m-1-col-on-xl{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-1-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-2-col-on-xl{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-2-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-3-col-on-xl{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-3-col--GridTemplateColumns--count)}}@media (min-width:1450px){.pf-c-description-list.pf-m-1-col-on-2xl{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-1-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-2-col-on-2xl{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-2-col--GridTemplateColumns--count)}.pf-c-description-list.pf-m-3-col-on-2xl{--pf-c-description-list--GridTemplateColumns--count:var(--pf-c-description-list--m-3-col--GridTemplateColumns--count)}}.pf-c-dual-list-selector{--pf-c-dual-list-selector__header--GridArea:pane-header;--pf-c-dual-list-selector__tools--GridArea:pane-tools;--pf-c-dual-list-selector__status--GridArea:pane-status;--pf-c-dual-list-selector__menu--GridArea:pane-menu;--pf-c-dual-list-selector__controls--GridArea:controls;--pf-c-dual-list-selector--m-chosen__header--GridArea:pane-header-c;--pf-c-dual-list-selector--m-chosen__tools--GridArea:pane-tools-c;--pf-c-dual-list-selector--m-chosen__status--GridArea:pane-status-c;--pf-c-dual-list-selector--m-chosen__menu--GridArea:pane-menu-c;--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--min:12.5rem;--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max:28.125rem;--pf-c-dual-list-selector__header--MarginBottom:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__title-text--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-dual-list-selector__tools--MarginBottom:var(--pf-global--spacer--md);--pf-c-dual-list-selector__tools-filter--tools-actions--MarginLeft:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__menu--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-dual-list-selector__menu--BorderColor:var(--pf-global--BorderColor--100);--pf-c-dual-list-selector__menu--MinHeight:12.5rem;--pf-c-dual-list-selector__menu--MaxHeight:20rem;--pf-c-dual-list-selector__item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item--PaddingRight:var(--pf-global--spacer--md);--pf-c-dual-list-selector__item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-dual-list-selector__item--FontSize:var(--pf-global--FontSize--sm);--pf-c-dual-list-selector__item--BackgroundColor:transparent;--pf-c-dual-list-selector__item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-dual-list-selector__item--focus-within--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-dual-list-selector__item--m-selected--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-dual-list-selector__item--m-expandable--PaddingLeft:0;--pf-c-dual-list-selector__item--indent--base:calc(var(--pf-global--spacer--md) + var(--pf-global--spacer--sm) + var(--pf-c-dual-list-selector__item--FontSize));--pf-c-dual-list-selector__item--nested-indent--base:calc(var(--pf-c-dual-list-selector__item--indent--base) - var(--pf-global--spacer--md));--pf-c-dual-list-selector__item-text--Color:var(--pf-global--Color--100);--pf-c-dual-list-selector__item--m-selected__text--Color:var(--pf-global--active-color--100);--pf-c-dual-list-selector__item--m-selected__text--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-dual-list-selector__status--MarginBottom:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__status-text--FontSize:var(--pf-global--FontSize--sm);--pf-c-dual-list-selector__status-text--Color:var(--pf-global--Color--200);--pf-c-dual-list-selector__controls--PaddingRight:var(--pf-global--spacer--md);--pf-c-dual-list-selector__controls--PaddingLeft:var(--pf-global--spacer--md);--pf-c-dual-list-selector__item-toggle--PaddingTop:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item-toggle--PaddingRight:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item-toggle--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item-toggle--PaddingLeft:var(--pf-global--spacer--md);--pf-c-dual-list-selector__item-toggle--MarginTop:calc(var(--pf-global--spacer--sm)*-1);--pf-c-dual-list-selector__item-toggle--MarginBottom:calc(var(--pf-global--spacer--sm)*-1);--pf-c-dual-list-selector__list__list__item-toggle--Left:0;--pf-c-dual-list-selector__list__list__item-toggle--TranslateX:-100%;--pf-c-dual-list-selector__item-check--MarginRight:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item-count--Marginleft:var(--pf-global--spacer--sm);--pf-c-dual-list-selector__item--c-badge--m-read--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-dual-list-selector__item-toggle-icon--Rotate:0;--pf-c-dual-list-selector__list-item--m-expanded__item-toggle-icon--Rotate:90deg;--pf-c-dual-list-selector__item-toggle-icon--Transition:var(--pf-global--Transition);--pf-c-dual-list-selector__item-toggle-icon--MinWidth:var(--pf-c-dual-list-selector__item--FontSize);display:grid;grid-template-areas:"pane-header . pane-header-c" "pane-tools . pane-tools-c" "pane-status . pane-status-c" "pane-menu controls pane-menu-c";grid-template-columns:minmax(var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--min),var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max)) min-content minmax(var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--min),var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max));grid-template-rows:repeat(3,auto) auto}.pf-c-dual-list-selector__pane{display:contents}.pf-c-dual-list-selector__pane.pf-m-chosen{--pf-c-dual-list-selector__header--GridArea:var(--pf-c-dual-list-selector--m-chosen__header--GridArea);--pf-c-dual-list-selector__tools--GridArea:var(--pf-c-dual-list-selector--m-chosen__tools--GridArea);--pf-c-dual-list-selector__status--GridArea:var(--pf-c-dual-list-selector--m-chosen__status--GridArea);--pf-c-dual-list-selector__menu--GridArea:var(--pf-c-dual-list-selector--m-chosen__menu--GridArea)}.pf-c-dual-list-selector__header{grid-area:var(--pf-c-dual-list-selector__header--GridArea);margin-bottom:var(--pf-c-dual-list-selector__header--MarginBottom)}.pf-c-dual-list-selector__title-text{font-weight:var(--pf-c-dual-list-selector__title-text--FontWeight)}.pf-c-dual-list-selector__tools{display:flex;grid-area:var(--pf-c-dual-list-selector__tools--GridArea);margin-bottom:var(--pf-c-dual-list-selector__tools--MarginBottom)}.pf-c-dual-list-selector__tools-filter{flex-grow:1}.pf-c-dual-list-selector__tools-actions{display:flex}.pf-c-dual-list-selector__tools-filter~.pf-c-dual-list-selector__tools-actions{margin-left:var(--pf-c-dual-list-selector__tools-filter--tools-actions--MarginLeft)}.pf-c-dual-list-selector__status{display:flex;grid-area:var(--pf-c-dual-list-selector__status--GridArea);margin-bottom:var(--pf-c-dual-list-selector__status--MarginBottom)}.pf-c-dual-list-selector__status-text{flex-grow:1;font-size:var(--pf-c-dual-list-selector__status-text--FontSize);color:var(--pf-c-dual-list-selector__status-text--Color)}.pf-c-dual-list-selector__menu{grid-area:var(--pf-c-dual-list-selector__menu--GridArea);min-height:var(--pf-c-dual-list-selector__menu--MinHeight);max-height:var(--pf-c-dual-list-selector__menu--MaxHeight);overflow:auto;border:var(--pf-c-dual-list-selector__menu--BorderWidth) solid var(--pf-c-dual-list-selector__menu--BorderColor)}.pf-c-dual-list-selector__list{display:flex;flex-direction:column}.pf-c-dual-list-selector__list .pf-c-dual-list-selector__list{--pf-c-dual-list-selector__item-toggle--MarginTop:0;--pf-c-dual-list-selector__item-toggle--MarginBottom:0}.pf-c-dual-list-selector__list .pf-c-dual-list-selector__list .pf-c-dual-list-selector__item-toggle{position:absolute;top:0;left:var(--pf-c-dual-list-selector__list__list__item-toggle--Left);transform:translateX(var(--pf-c-dual-list-selector__list__list__item-toggle--TranslateX))}.pf-c-dual-list-selector__list-item.pf-m-expandable{--pf-c-dual-list-selector__item--PaddingLeft:var(--pf-c-dual-list-selector__item--m-expandable--PaddingLeft)}.pf-c-dual-list-selector__list-item.pf-m-expanded>.pf-c-dual-list-selector__item{--pf-c-dual-list-selector__item-toggle-icon--Rotate:var(--pf-c-dual-list-selector__list-item--m-expanded__item-toggle-icon--Rotate)}.pf-c-dual-list-selector__item,.pf-c-dual-list-selector__main{display:flex}.pf-c-dual-list-selector__item,.pf-c-dual-list-selector__item-main{flex-basis:100%}.pf-c-dual-list-selector__item{position:relative;width:100%;padding:var(--pf-c-dual-list-selector__item--PaddingTop) var(--pf-c-dual-list-selector__item--PaddingRight) var(--pf-c-dual-list-selector__item--PaddingBottom) var(--pf-c-dual-list-selector__item--PaddingLeft);font-size:var(--pf-c-dual-list-selector__item--FontSize);text-align:left;cursor:pointer;background-color:var(--pf-c-dual-list-selector__item--BackgroundColor);border:0}.pf-c-dual-list-selector__item:hover{--pf-c-dual-list-selector__item--BackgroundColor:var(--pf-c-dual-list-selector__item--hover--BackgroundColor)}.pf-c-dual-list-selector__item:focus-within{--pf-c-dual-list-selector__item--BackgroundColor:var(--pf-c-dual-list-selector__item--focus-within--BackgroundColor)}.pf-c-dual-list-selector__item.pf-m-selected{--pf-c-dual-list-selector__item--BackgroundColor:var(--pf-c-dual-list-selector__item--m-selected--BackgroundColor)}.pf-c-dual-list-selector__item.pf-m-selected .pf-c-dual-list-selector__item-text{--pf-c-dual-list-selector__item-text--Color:var(--pf-c-dual-list-selector__item--m-selected__text--Color);font-weight:var(--pf-c-dual-list-selector__item--m-selected__text--FontWeight)}.pf-c-dual-list-selector__item.pf-m-check{--pf-c-dual-list-selector__item--m-selected--BackgroundColor:transparent}.pf-c-dual-list-selector__item .pf-c-dual-list-selector__item-count{margin-left:var(--pf-c-dual-list-selector__item-count--Marginleft)}.pf-c-dual-list-selector__item .pf-c-dual-list-selector__item-count .pf-c-badge.pf-m-read{--pf-c-badge--m-read--BackgroundColor:var(--pf-c-dual-list-selector__item--c-badge--m-read--BackgroundColor)}.pf-c-dual-list-selector__item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-grow:1;color:var(--pf-c-dual-list-selector__item-text--Color)}.pf-c-dual-list-selector__controls{grid-area:var(--pf-c-dual-list-selector__controls--GridArea);align-self:center;padding-right:var(--pf-c-dual-list-selector__controls--PaddingRight);padding-left:var(--pf-c-dual-list-selector__controls--PaddingLeft)}.pf-c-dual-list-selector__item-main{display:flex;min-width:0}.pf-c-dual-list-selector__item-toggle{padding:var(--pf-c-dual-list-selector__item-toggle--PaddingTop) var(--pf-c-dual-list-selector__item-toggle--PaddingRight) var(--pf-c-dual-list-selector__item-toggle--PaddingBottom) var(--pf-c-dual-list-selector__item-toggle--PaddingLeft);margin-top:var(--pf-c-dual-list-selector__item-toggle--MarginTop);margin-bottom:var(--pf-c-dual-list-selector__item-toggle--MarginBottom)}.pf-c-dual-list-selector__item-check{display:flex;align-items:center;margin-right:var(--pf-c-dual-list-selector__item-check--MarginRight)}.pf-c-dual-list-selector__item-toggle-icon{display:inline-block;min-width:var(--pf-c-dual-list-selector__item-toggle-icon--MinWidth);text-align:center;transition:var(--pf-c-dual-list-selector__item-toggle-icon--Transition);transform:rotate(var(--pf-c-dual-list-selector__item-toggle-icon--Rotate))}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*1 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*2 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*3 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*4 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*5 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*6 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*7 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*8 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*9 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item{--pf-c-dual-list-selector__item--PaddingLeft:calc(var(--pf-c-dual-list-selector__item--nested-indent--base)*10 + var(--pf-c-dual-list-selector__item--indent--base));--pf-c-dual-list-selector__list__list__item-toggle--Left:var(--pf-c-dual-list-selector__item--PaddingLeft)}.pf-c-toolbar{--pf-c-toolbar--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-toolbar--RowGap:var(--pf-global--spacer--lg);--pf-c-toolbar--PaddingTop:var(--pf-global--spacer--md);--pf-c-toolbar--PaddingBottom:var(--pf-global--spacer--md);--pf-c-toolbar__content--PaddingRight:var(--pf-global--spacer--md);--pf-c-toolbar__content--PaddingLeft:var(--pf-global--spacer--md);--pf-c-toolbar--m-page-insets--inset:var(--pf-global--spacer--md);--pf-c-toolbar--m-page-insets--xl--inset:var(--pf-global--spacer--lg);--pf-c-toolbar__expandable-content--PaddingTop:var(--pf-c-toolbar--RowGap);--pf-c-toolbar__expandable-content--PaddingRight:var(--pf-c-toolbar__content--PaddingRight);--pf-c-toolbar__expandable-content--PaddingBottom:var(--pf-global--spacer--md);--pf-c-toolbar__expandable-content--PaddingLeft:var(--pf-c-toolbar__content--PaddingLeft);--pf-c-toolbar__expandable-content--lg--PaddingRight:0;--pf-c-toolbar__expandable-content--lg--PaddingBottom:0;--pf-c-toolbar__expandable-content--lg--PaddingLeft:0;--pf-c-toolbar__expandable-content--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-toolbar__expandable-content--BoxShadow:var(--pf-global--BoxShadow--md-bottom);--pf-c-toolbar__expandable-content--BackgroundColor:var(--pf-c-toolbar--BackgroundColor);--pf-c-toolbar__expandable-content--m-expanded--GridRowGap:var(--pf-global--gutter--md);--pf-c-toolbar__group--m-chip-container--MarginTop:calc(var(--pf-global--spacer--md)*-1);--pf-c-toolbar__group--m-chip-container__item--MarginTop:var(--pf-global--spacer--md);--pf-c-toolbar--spacer--base:var(--pf-global--spacer--md);--pf-c-toolbar__item--spacer:var(--pf-c-toolbar--spacer--base);--pf-c-toolbar__group--spacer:var(--pf-c-toolbar--spacer--base);--pf-c-toolbar__group--m-toggle-group--spacer:var(--pf-global--spacer--sm);--pf-c-toolbar__group--m-toggle-group--m-show--spacer:var(--pf-c-toolbar__group--spacer);--pf-c-toolbar__group--m-icon-button-group--spacer:var(--pf-c-toolbar__group--spacer);--pf-c-toolbar__group--m-icon-button-group--space-items:0;--pf-c-toolbar__group--m-button-group--spacer:var(--pf-c-toolbar__group--spacer);--pf-c-toolbar__group--m-button-group--space-items:var(--pf-global--spacer--sm);--pf-c-toolbar__group--m-filter-group--spacer:var(--pf-c-toolbar__group--spacer);--pf-c-toolbar__group--m-filter-group--space-items:0;--pf-c-toolbar__item--m-overflow-menu--spacer:var(--pf-c-toolbar__item--spacer);--pf-c-toolbar__item--m-bulk-select--spacer:var(--pf-global--spacer--lg);--pf-c-toolbar__expand-all-icon--Rotate:0;--pf-c-toolbar__expand-all-icon--Transition:var(--pf-global--Transition);--pf-c-toolbar__item--m-expand-all--m-expanded__expand-all-icon--Rotate:90deg;--pf-c-toolbar__item--m-search-filter--spacer:var(--pf-global--spacer--sm);--pf-c-toolbar__item--m-chip-group--spacer:var(--pf-global--spacer--sm);--pf-c-toolbar__item--m-label--spacer:var(--pf-c-toolbar__item--spacer);--pf-c-toolbar__item--m-label--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-toolbar__toggle--m-expanded__c-button--m-plain--Color:var(--pf-global--Color--100);--pf-c-toolbar--c-divider--m-vertical--spacer:var(--pf-c-toolbar--spacer--base);position:relative;row-gap:var(--pf-c-toolbar--RowGap);display:grid;padding-top:var(--pf-c-toolbar--PaddingTop);padding-bottom:var(--pf-c-toolbar--PaddingBottom);background-color:var(--pf-c-toolbar--BackgroundColor)}@media screen and (min-width:992px){.pf-c-toolbar{--pf-c-toolbar__expandable-content--PaddingRight:var(--pf-c-toolbar__expandable-content--lg--PaddingRight);--pf-c-toolbar__expandable-content--PaddingBottom:var(--pf-c-toolbar__expandable-content--lg--PaddingBottom);--pf-c-toolbar__expandable-content--PaddingLeft:var(--pf-c-toolbar__expandable-content--lg--PaddingLeft)}}@media screen and (min-width:1200px){.pf-c-toolbar{--pf-c-toolbar--m-page-insets--inset:var(--pf-c-toolbar--m-page-insets--xl--inset)}}.pf-c-toolbar.pf-m-page-insets{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--m-page-insets--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--m-page-insets--inset)}.pf-c-toolbar__content-section>.pf-c-divider,.pf-c-toolbar__group>.pf-c-divider{--pf-c-toolbar--spacer:var(--pf-c-toolbar--c-divider--m-vertical--spacer)}.pf-c-toolbar__content-section>.pf-c-divider.pf-m-vertical,.pf-c-toolbar__group>.pf-c-divider.pf-m-vertical{margin-right:var(--pf-c-toolbar--spacer)}.pf-c-toolbar__content-section>.pf-c-divider.pf-m-vertical:last-child,.pf-c-toolbar__group>.pf-c-divider.pf-m-vertical:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar__group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--spacer);display:flex;align-items:center;margin-right:var(--pf-c-toolbar--spacer)}.pf-c-toolbar__group.pf-m-button-group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-button-group--spacer)}.pf-c-toolbar__group.pf-m-button-group>*{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-button-group--space-items)}.pf-c-toolbar__group.pf-m-icon-button-group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-icon-button-group--spacer)}.pf-c-toolbar__group.pf-m-icon-button-group>*{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-icon-button-group--space-items)}.pf-c-toolbar__group.pf-m-filter-group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-filter-group--spacer)}.pf-c-toolbar__group.pf-m-filter-group>*{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-filter-group--space-items)}.pf-c-toolbar__group.pf-m-filter-group>*+*{margin-left:-1px}.pf-c-toolbar__group.pf-m-toggle-group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--spacer)}.pf-c-toolbar__group.pf-m-toggle-group .pf-c-toolbar__group,.pf-c-toolbar__group.pf-m-toggle-group .pf-c-toolbar__item{display:none;visibility:hidden}.pf-c-toolbar__group.pf-m-toggle-group .pf-c-toolbar__toggle{display:inline-block;visibility:visible}.pf-c-toolbar__group:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar__item{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--spacer);margin-right:var(--pf-c-toolbar--spacer)}.pf-c-toolbar__item.pf-m-overflow-menu{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--m-overflow-menu--spacer)}.pf-c-toolbar__item.pf-m-bulk-select{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--m-bulk-select--spacer)}.pf-c-toolbar__item.pf-m-expand-all.pf-m-expanded{--pf-c-toolbar__expand-all-icon--Rotate:var(--pf-c-toolbar__item--m-expand-all--m-expanded__expand-all-icon--Rotate)}.pf-c-toolbar__item.pf-m-search-filter{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--m-search-filter--spacer)}.pf-c-toolbar__item.pf-m-chip-group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--m-chip-group--spacer)}.pf-c-toolbar__item.pf-m-label{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--m-label--spacer);font-weight:var(--pf-c-toolbar__item--m-label--FontWeight)}.pf-c-toolbar__item.pf-m-pagination{margin-left:auto}.pf-c-toolbar__item.pf-m-pagination .pf-c-pagination{flex-wrap:nowrap}.pf-c-toolbar__item:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar__expand-all-icon{display:inline-block;transition:var(--pf-c-toolbar__expand-all-icon--Transition);transform:rotate(var(--pf-c-toolbar__expand-all-icon--Rotate))}.pf-c-toolbar__content,.pf-c-toolbar__content-section{display:flex;flex-wrap:wrap;align-items:center}.pf-c-toolbar__content{position:relative;padding-right:var(--pf-c-toolbar__content--PaddingRight);padding-left:var(--pf-c-toolbar__content--PaddingLeft)}.pf-c-toolbar__content-section{width:100%}.pf-c-toolbar__expandable-content{position:absolute;top:100%;right:0;left:0;z-index:var(--pf-c-toolbar__expandable-content--ZIndex);display:none;width:100%;padding:var(--pf-c-toolbar__expandable-content--PaddingTop) var(--pf-c-toolbar__expandable-content--PaddingRight) var(--pf-c-toolbar__expandable-content--PaddingBottom) var(--pf-c-toolbar__expandable-content--PaddingLeft);visibility:hidden;background-color:var(--pf-c-toolbar__expandable-content--BackgroundColor);box-shadow:var(--pf-c-toolbar__expandable-content--BoxShadow)}@media screen and (min-width:992px){.pf-c-toolbar__expandable-content{position:static;box-shadow:none}}.pf-c-toolbar__expandable-content.pf-m-expanded{display:grid;grid-row-gap:var(--pf-c-toolbar__expandable-content--m-expanded--GridRowGap);visibility:visible}.pf-c-toolbar__expandable-content .pf-c-toolbar__group,.pf-c-toolbar__expandable-content .pf-c-toolbar__item{--pf-c-toolbar--spacer:0}.pf-c-toolbar__expandable-content .pf-c-toolbar__group{display:grid;grid-row-gap:var(--pf-c-toolbar__expandable-content--m-expanded--GridRowGap)}.pf-c-toolbar__expandable-content .pf-m-label{display:none;visibility:hidden}.pf-c-toolbar__content.pf-m-chip-container,.pf-c-toolbar__group.pf-m-chip-container{display:flex;flex-wrap:wrap;align-items:baseline;margin-top:var(--pf-c-toolbar__group--m-chip-container--MarginTop);grid-row-gap:0}.pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__item,.pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__item{--pf-c-toolbar--spacer:var(--pf-c-toolbar__item--spacer);margin-top:var(--pf-c-toolbar__group--m-chip-container__item--MarginTop)}.pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__group,.pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__group{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--spacer);display:flex;flex-wrap:wrap;grid-row-gap:0}.pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__group:last-child,.pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__item:last-child,.pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__group:last-child,.pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__item:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-c-chip-group:last-child{--pf-c-chip-group--MarginRight:0}.pf-c-toolbar .pf-c-chip-group li:last-child{--pf-c-chip-group__li--m-toolbar--MarginRight:0}.pf-c-toolbar__toggle.pf-m-expanded .pf-c-button.pf-m-plain{color:var(--pf-c-toolbar__toggle--m-expanded__c-button--m-plain--Color)}.pf-m-toggle-group.pf-m-show{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer)}.pf-m-toggle-group.pf-m-show .pf-c-toolbar__group,.pf-m-toggle-group.pf-m-show .pf-c-toolbar__item{display:flex;flex:0 1 auto;visibility:visible}.pf-m-toggle-group.pf-m-show .pf-c-toolbar__toggle{display:none;visibility:hidden}@media (min-width:576px){.pf-m-toggle-group.pf-m-show-on-sm{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer)}.pf-m-toggle-group.pf-m-show-on-sm .pf-c-toolbar__group,.pf-m-toggle-group.pf-m-show-on-sm .pf-c-toolbar__item{display:flex;flex:0 1 auto;visibility:visible}.pf-m-toggle-group.pf-m-show-on-sm .pf-c-toolbar__toggle{display:none;visibility:hidden}}@media (min-width:768px){.pf-m-toggle-group.pf-m-show-on-md{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer)}.pf-m-toggle-group.pf-m-show-on-md .pf-c-toolbar__group,.pf-m-toggle-group.pf-m-show-on-md .pf-c-toolbar__item{display:flex;flex:0 1 auto;visibility:visible}.pf-m-toggle-group.pf-m-show-on-md .pf-c-toolbar__toggle{display:none;visibility:hidden}}@media (min-width:992px){.pf-m-toggle-group.pf-m-show-on-lg{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer)}.pf-m-toggle-group.pf-m-show-on-lg .pf-c-toolbar__group,.pf-m-toggle-group.pf-m-show-on-lg .pf-c-toolbar__item{display:flex;flex:0 1 auto;visibility:visible}.pf-m-toggle-group.pf-m-show-on-lg .pf-c-toolbar__toggle{display:none;visibility:hidden}}@media (min-width:1200px){.pf-m-toggle-group.pf-m-show-on-xl{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer)}.pf-m-toggle-group.pf-m-show-on-xl .pf-c-toolbar__group,.pf-m-toggle-group.pf-m-show-on-xl .pf-c-toolbar__item{display:flex;flex:0 1 auto;visibility:visible}.pf-m-toggle-group.pf-m-show-on-xl .pf-c-toolbar__toggle{display:none;visibility:hidden}}@media (min-width:1450px){.pf-m-toggle-group.pf-m-show-on-2xl{--pf-c-toolbar--spacer:var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer)}.pf-m-toggle-group.pf-m-show-on-2xl .pf-c-toolbar__group,.pf-m-toggle-group.pf-m-show-on-2xl .pf-c-toolbar__item{display:flex;flex:0 1 auto;visibility:visible}.pf-m-toggle-group.pf-m-show-on-2xl .pf-c-toolbar__toggle{display:none;visibility:hidden}}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right{margin-left:auto}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left,.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right~.pf-m-pagination{margin-left:0}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left~.pf-m-pagination{margin-left:auto}.pf-c-toolbar .pf-m-hidden{display:none;visibility:hidden}.pf-c-toolbar .pf-m-visible{display:flex;visibility:visible}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap,.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap{flex-wrap:nowrap}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap,.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap{flex-wrap:wrap}@media (min-width:576px){.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-sm,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-sm{margin-left:auto}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-sm,.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-sm~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-sm,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-sm~.pf-m-pagination{margin-left:0}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-sm~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-sm~.pf-m-pagination{margin-left:auto}.pf-c-toolbar .pf-m-hidden-on-sm{display:none;visibility:hidden}.pf-c-toolbar .pf-m-visible-on-sm{display:flex;visibility:visible}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-sm,.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-sm{flex-wrap:nowrap}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-sm,.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-sm{flex-wrap:wrap}}@media (min-width:768px){.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-md,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-md{margin-left:auto}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-md,.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-md~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-md,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-md~.pf-m-pagination{margin-left:0}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-md~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-md~.pf-m-pagination{margin-left:auto}.pf-c-toolbar .pf-m-hidden-on-md{display:none;visibility:hidden}.pf-c-toolbar .pf-m-visible-on-md{display:flex;visibility:visible}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-md,.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-md{flex-wrap:nowrap}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-md,.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-md{flex-wrap:wrap}}@media (min-width:992px){.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-lg,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-lg{margin-left:auto}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-lg,.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-lg~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-lg,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-lg~.pf-m-pagination{margin-left:0}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-lg~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-lg~.pf-m-pagination{margin-left:auto}.pf-c-toolbar .pf-m-hidden-on-lg{display:none;visibility:hidden}.pf-c-toolbar .pf-m-visible-on-lg{display:flex;visibility:visible}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-lg,.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-lg{flex-wrap:nowrap}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-lg,.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-lg{flex-wrap:wrap}}@media (min-width:1200px){.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-xl,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-xl{margin-left:auto}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-xl,.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-xl~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-xl,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-xl~.pf-m-pagination{margin-left:0}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-xl~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-xl~.pf-m-pagination{margin-left:auto}.pf-c-toolbar .pf-m-hidden-on-xl{display:none;visibility:hidden}.pf-c-toolbar .pf-m-visible-on-xl{display:flex;visibility:visible}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-xl,.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-xl{flex-wrap:nowrap}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-xl,.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-xl{flex-wrap:wrap}}@media (min-width:1450px){.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-2xl,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-2xl{margin-left:auto}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-2xl,.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-2xl~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-2xl,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-2xl~.pf-m-pagination{margin-left:0}.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-2xl~.pf-m-pagination,.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-2xl~.pf-m-pagination{margin-left:auto}.pf-c-toolbar .pf-m-hidden-on-2xl{display:none;visibility:hidden}.pf-c-toolbar .pf-m-visible-on-2xl{display:flex;visibility:visible}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-2xl,.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-2xl{flex-wrap:nowrap}.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-2xl,.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-2xl{flex-wrap:wrap}}.pf-c-toolbar .pf-m-space-items-none>*,.pf-c-toolbar .pf-m-space-items-none>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-sm>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-space-items-sm>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-md>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-space-items-md>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-lg>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}.pf-c-toolbar .pf-m-space-items-lg>:last-child{--pf-c-toolbar--spacer:0}@media (min-width:576px){.pf-c-toolbar .pf-m-space-items-none-on-sm>*,.pf-c-toolbar .pf-m-space-items-none-on-sm>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-sm-on-sm>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-space-items-sm-on-sm>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-md-on-sm>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-space-items-md-on-sm>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-lg-on-sm>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}.pf-c-toolbar .pf-m-space-items-lg-on-sm>:last-child{--pf-c-toolbar--spacer:0}}@media (min-width:768px){.pf-c-toolbar .pf-m-space-items-none-on-md>*,.pf-c-toolbar .pf-m-space-items-none-on-md>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-sm-on-md>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-space-items-sm-on-md>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-md-on-md>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-space-items-md-on-md>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-lg-on-md>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}.pf-c-toolbar .pf-m-space-items-lg-on-md>:last-child{--pf-c-toolbar--spacer:0}}@media (min-width:992px){.pf-c-toolbar .pf-m-space-items-none-on-lg>*,.pf-c-toolbar .pf-m-space-items-none-on-lg>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-sm-on-lg>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-space-items-sm-on-lg>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-md-on-lg>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-space-items-md-on-lg>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-lg-on-lg>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}.pf-c-toolbar .pf-m-space-items-lg-on-lg>:last-child{--pf-c-toolbar--spacer:0}}@media (min-width:1200px){.pf-c-toolbar .pf-m-space-items-none-on-xl>*,.pf-c-toolbar .pf-m-space-items-none-on-xl>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-sm-on-xl>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-space-items-sm-on-xl>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-md-on-xl>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-space-items-md-on-xl>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-lg-on-xl>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}.pf-c-toolbar .pf-m-space-items-lg-on-xl>:last-child{--pf-c-toolbar--spacer:0}}@media (min-width:1450px){.pf-c-toolbar .pf-m-space-items-none-on-2xl>*,.pf-c-toolbar .pf-m-space-items-none-on-2xl>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-sm-on-2xl>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-space-items-sm-on-2xl>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-md-on-2xl>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-space-items-md-on-2xl>:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-space-items-lg-on-2xl>*{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}.pf-c-toolbar .pf-m-space-items-lg-on-2xl>:last-child{--pf-c-toolbar--spacer:0}}.pf-c-toolbar .pf-m-spacer-none,.pf-c-toolbar .pf-m-spacer-none:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-spacer-sm,.pf-c-toolbar .pf-m-spacer-sm:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-spacer-md,.pf-c-toolbar .pf-m-spacer-md:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-spacer-lg,.pf-c-toolbar .pf-m-spacer-lg:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}@media (min-width:576px){.pf-c-toolbar .pf-m-spacer-none-on-sm,.pf-c-toolbar .pf-m-spacer-none-on-sm:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-spacer-sm-on-sm,.pf-c-toolbar .pf-m-spacer-sm-on-sm:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-spacer-md-on-sm,.pf-c-toolbar .pf-m-spacer-md-on-sm:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-spacer-lg-on-sm,.pf-c-toolbar .pf-m-spacer-lg-on-sm:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}}@media (min-width:768px){.pf-c-toolbar .pf-m-spacer-none-on-md,.pf-c-toolbar .pf-m-spacer-none-on-md:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-spacer-sm-on-md,.pf-c-toolbar .pf-m-spacer-sm-on-md:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-spacer-md-on-md,.pf-c-toolbar .pf-m-spacer-md-on-md:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-spacer-lg-on-md,.pf-c-toolbar .pf-m-spacer-lg-on-md:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}}@media (min-width:992px){.pf-c-toolbar .pf-m-spacer-none-on-lg,.pf-c-toolbar .pf-m-spacer-none-on-lg:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-spacer-sm-on-lg,.pf-c-toolbar .pf-m-spacer-sm-on-lg:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-spacer-md-on-lg,.pf-c-toolbar .pf-m-spacer-md-on-lg:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-spacer-lg-on-lg,.pf-c-toolbar .pf-m-spacer-lg-on-lg:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}}@media (min-width:1200px){.pf-c-toolbar .pf-m-spacer-none-on-xl,.pf-c-toolbar .pf-m-spacer-none-on-xl:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-spacer-sm-on-xl,.pf-c-toolbar .pf-m-spacer-sm-on-xl:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-spacer-md-on-xl,.pf-c-toolbar .pf-m-spacer-md-on-xl:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-spacer-lg-on-xl,.pf-c-toolbar .pf-m-spacer-lg-on-xl:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}}@media (min-width:1450px){.pf-c-toolbar .pf-m-spacer-none-on-2xl,.pf-c-toolbar .pf-m-spacer-none-on-2xl:last-child{--pf-c-toolbar--spacer:0}.pf-c-toolbar .pf-m-spacer-sm-on-2xl,.pf-c-toolbar .pf-m-spacer-sm-on-2xl:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--sm)}.pf-c-toolbar .pf-m-spacer-md-on-2xl,.pf-c-toolbar .pf-m-spacer-md-on-2xl:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--md)}.pf-c-toolbar .pf-m-spacer-lg-on-2xl,.pf-c-toolbar .pf-m-spacer-lg-on-2xl:last-child{--pf-c-toolbar--spacer:var(--pf-global--spacer--lg)}}.pf-c-toolbar.pf-m-inset-none{--pf-c-toolbar--inset:0}.pf-c-toolbar.pf-m-inset-none,.pf-c-toolbar.pf-m-inset-sm{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-sm{--pf-c-toolbar--inset:var(--pf-global--spacer--sm)}.pf-c-toolbar.pf-m-inset-md{--pf-c-toolbar--inset:var(--pf-global--spacer--md)}.pf-c-toolbar.pf-m-inset-lg,.pf-c-toolbar.pf-m-inset-md{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-lg{--pf-c-toolbar--inset:var(--pf-global--spacer--lg)}.pf-c-toolbar.pf-m-inset-xl{--pf-c-toolbar--inset:var(--pf-global--spacer--xl)}.pf-c-toolbar.pf-m-inset-2xl,.pf-c-toolbar.pf-m-inset-xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-2xl{--pf-c-toolbar--inset:var(--pf-global--spacer--2xl)}@media (min-width:576px){.pf-c-toolbar.pf-m-inset-none-on-sm{--pf-c-toolbar--inset:0}.pf-c-toolbar.pf-m-inset-none-on-sm,.pf-c-toolbar.pf-m-inset-sm-on-sm{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-sm-on-sm{--pf-c-toolbar--inset:var(--pf-global--spacer--sm)}.pf-c-toolbar.pf-m-inset-md-on-sm{--pf-c-toolbar--inset:var(--pf-global--spacer--md)}.pf-c-toolbar.pf-m-inset-lg-on-sm,.pf-c-toolbar.pf-m-inset-md-on-sm{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-lg-on-sm{--pf-c-toolbar--inset:var(--pf-global--spacer--lg)}.pf-c-toolbar.pf-m-inset-xl-on-sm{--pf-c-toolbar--inset:var(--pf-global--spacer--xl)}.pf-c-toolbar.pf-m-inset-2xl-on-sm,.pf-c-toolbar.pf-m-inset-xl-on-sm{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-2xl-on-sm{--pf-c-toolbar--inset:var(--pf-global--spacer--2xl)}}@media (min-width:768px){.pf-c-toolbar.pf-m-inset-none-on-md{--pf-c-toolbar--inset:0}.pf-c-toolbar.pf-m-inset-none-on-md,.pf-c-toolbar.pf-m-inset-sm-on-md{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-sm-on-md{--pf-c-toolbar--inset:var(--pf-global--spacer--sm)}.pf-c-toolbar.pf-m-inset-md-on-md{--pf-c-toolbar--inset:var(--pf-global--spacer--md)}.pf-c-toolbar.pf-m-inset-lg-on-md,.pf-c-toolbar.pf-m-inset-md-on-md{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-lg-on-md{--pf-c-toolbar--inset:var(--pf-global--spacer--lg)}.pf-c-toolbar.pf-m-inset-xl-on-md{--pf-c-toolbar--inset:var(--pf-global--spacer--xl)}.pf-c-toolbar.pf-m-inset-2xl-on-md,.pf-c-toolbar.pf-m-inset-xl-on-md{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-2xl-on-md{--pf-c-toolbar--inset:var(--pf-global--spacer--2xl)}}@media (min-width:992px){.pf-c-toolbar.pf-m-inset-none-on-lg{--pf-c-toolbar--inset:0}.pf-c-toolbar.pf-m-inset-none-on-lg,.pf-c-toolbar.pf-m-inset-sm-on-lg{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-sm-on-lg{--pf-c-toolbar--inset:var(--pf-global--spacer--sm)}.pf-c-toolbar.pf-m-inset-md-on-lg{--pf-c-toolbar--inset:var(--pf-global--spacer--md)}.pf-c-toolbar.pf-m-inset-lg-on-lg,.pf-c-toolbar.pf-m-inset-md-on-lg{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-lg-on-lg{--pf-c-toolbar--inset:var(--pf-global--spacer--lg)}.pf-c-toolbar.pf-m-inset-xl-on-lg{--pf-c-toolbar--inset:var(--pf-global--spacer--xl)}.pf-c-toolbar.pf-m-inset-2xl-on-lg,.pf-c-toolbar.pf-m-inset-xl-on-lg{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-2xl-on-lg{--pf-c-toolbar--inset:var(--pf-global--spacer--2xl)}}@media (min-width:1200px){.pf-c-toolbar.pf-m-inset-none-on-xl{--pf-c-toolbar--inset:0}.pf-c-toolbar.pf-m-inset-none-on-xl,.pf-c-toolbar.pf-m-inset-sm-on-xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-sm-on-xl{--pf-c-toolbar--inset:var(--pf-global--spacer--sm)}.pf-c-toolbar.pf-m-inset-md-on-xl{--pf-c-toolbar--inset:var(--pf-global--spacer--md)}.pf-c-toolbar.pf-m-inset-lg-on-xl,.pf-c-toolbar.pf-m-inset-md-on-xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-lg-on-xl{--pf-c-toolbar--inset:var(--pf-global--spacer--lg)}.pf-c-toolbar.pf-m-inset-xl-on-xl{--pf-c-toolbar--inset:var(--pf-global--spacer--xl)}.pf-c-toolbar.pf-m-inset-2xl-on-xl,.pf-c-toolbar.pf-m-inset-xl-on-xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-2xl-on-xl{--pf-c-toolbar--inset:var(--pf-global--spacer--2xl)}}@media (min-width:1450px){.pf-c-toolbar.pf-m-inset-none-on-2xl{--pf-c-toolbar--inset:0}.pf-c-toolbar.pf-m-inset-none-on-2xl,.pf-c-toolbar.pf-m-inset-sm-on-2xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-sm-on-2xl{--pf-c-toolbar--inset:var(--pf-global--spacer--sm)}.pf-c-toolbar.pf-m-inset-md-on-2xl{--pf-c-toolbar--inset:var(--pf-global--spacer--md)}.pf-c-toolbar.pf-m-inset-lg-on-2xl,.pf-c-toolbar.pf-m-inset-md-on-2xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-lg-on-2xl{--pf-c-toolbar--inset:var(--pf-global--spacer--lg)}.pf-c-toolbar.pf-m-inset-xl-on-2xl{--pf-c-toolbar--inset:var(--pf-global--spacer--xl)}.pf-c-toolbar.pf-m-inset-2xl-on-2xl,.pf-c-toolbar.pf-m-inset-xl-on-2xl{--pf-c-toolbar__content--PaddingRight:var(--pf-c-toolbar--inset);--pf-c-toolbar__content--PaddingLeft:var(--pf-c-toolbar--inset)}.pf-c-toolbar.pf-m-inset-2xl-on-2xl{--pf-c-toolbar--inset:var(--pf-global--spacer--2xl)}}.pf-c-toolbar__content-section>:last-child{--pf-c-toolbar--spacer:0}.pf-c-date-picker{--pf-c-date-picker--m-top__calendar--Top:0;--pf-c-date-picker--m-top__calendar--TranslateY:calc(-100% - var(--pf-global--spacer--xs));--pf-c-date-picker__helper-text--MarginTop:var(--pf-global--spacer--xs);--pf-c-date-picker__helper-text--FontSize:var(--pf-global--FontSize--sm);--pf-c-date-picker__helper-text--Color:var(--pf-global--Color--100);--pf-c-date-picker__helper-text--m-error--Color:var(--pf-global--danger-color--100);--pf-c-date-picker__input--c-form-control--Width:calc(var(--pf-c-date-picker__input--c-form-control--width-chars)*1ch + var(--pf-c-date-picker__input--c-form-control--width-base));--pf-c-date-picker__input--c-form-control--width-base:calc(var(--pf-global--spacer--xl) + var(--pf-global--spacer--sm));--pf-c-date-picker__input--c-form-control--width-chars:10;--pf-c-date-picker__calendar--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-date-picker__calendar--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-date-picker__calendar--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-date-picker__calendar--Top:calc(100% + var(--pf-global--spacer--xs));--pf-c-date-picker__calendar--Right:auto;--pf-c-date-picker__calendar--Left:0;--pf-c-date-picker__calendar--m-align-right--Right:0;--pf-c-date-picker__calendar--m-align-right--Left:auto;position:relative;display:inline-block}.pf-c-date-picker__helper-text{margin-top:var(--pf-c-date-picker__helper-text--MarginTop);font-size:var(--pf-c-date-picker__helper-text--FontSize);color:var(--pf-c-date-picker__helper-text--Color)}.pf-c-date-picker__helper-text.pf-m-error{--pf-c-date-picker__helper-text--Color:var(--pf-c-date-picker__helper-text--m-error--Color)}.pf-c-date-picker__input .pf-c-form-control{width:var(--pf-c-date-picker__input--c-form-control--Width)}.pf-c-date-picker__calendar{position:absolute;top:var(--pf-c-date-picker__calendar--Top);right:var(--pf-c-date-picker__calendar--Right);left:var(--pf-c-date-picker__calendar--Left);z-index:var(--pf-c-date-picker__calendar--ZIndex);background-color:var(--pf-c-date-picker__calendar--BackgroundColor);box-shadow:var(--pf-c-date-picker__calendar--BoxShadow)}.pf-c-date-picker__calendar.pf-m-align-right{--pf-c-date-picker__calendar--Right:var(--pf-c-date-picker__calendar--m-align-right--Right);--pf-c-date-picker__calendar--Left:var(--pf-c-date-picker__calendar--m-align-right--Left)}.pf-c-date-picker.pf-m-top .pf-c-date-picker__calendar{--pf-c-date-picker__calendar--Top:var(--pf-c-date-picker--m-top__calendar--Top);transform:translateY(var(--pf-c-date-picker--m-top__calendar--TranslateY))}.pf-c-divider{--pf-c-divider--Height:var(--pf-global--BorderWidth--sm);--pf-c-divider--BackgroundColor:var(--pf-global--BorderColor--100);--pf-c-divider--after--Height:var(--pf-c-divider--Height);--pf-c-divider--after--BackgroundColor:var(--pf-c-divider--BackgroundColor);--pf-c-divider--after--FlexBasis:100%;--pf-c-divider--after--Inset:0%;--pf-c-divider--m-vertical--after--FlexBasis:100%;--pf-c-divider--m-vertical--after--Width:var(--pf-global--BorderWidth--sm);display:flex;align-items:center;align-self:stretch;justify-content:center;width:100%;border:0}.pf-c-divider:after{flex-basis:calc(var(--pf-c-divider--after--FlexBasis) - var(--pf-c-divider--after--Inset)*2);align-self:stretch;height:var(--pf-c-divider--after--Height);content:"";background-color:var(--pf-c-divider--after--BackgroundColor);justify-self:center}.pf-c-divider.pf-m-vertical{display:inline-flex;flex-direction:column;width:auto;height:inherit;min-height:100%;max-height:100%}.pf-c-divider.pf-m-vertical:after{flex-basis:calc(var(--pf-c-divider--m-vertical--after--FlexBasis) - var(--pf-c-divider--after--Inset));width:var(--pf-c-divider--m-vertical--after--Width)}.pf-c-divider.pf-m-inset-none{--pf-c-divider--after--Inset:0%}.pf-c-divider.pf-m-inset-xs{--pf-c-divider--after--Inset:var(--pf-global--spacer--xs)}.pf-c-divider.pf-m-inset-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--sm)}.pf-c-divider.pf-m-inset-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--md)}.pf-c-divider.pf-m-inset-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--lg)}.pf-c-divider.pf-m-inset-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--xl)}.pf-c-divider.pf-m-inset-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--2xl)}.pf-c-divider.pf-m-inset-3xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--3xl)}@media (min-width:576px){.pf-c-divider.pf-m-inset-none-on-sm{--pf-c-divider--after--Inset:0%}.pf-c-divider.pf-m-inset-xs-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--xs)}.pf-c-divider.pf-m-inset-sm-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--sm)}.pf-c-divider.pf-m-inset-md-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--md)}.pf-c-divider.pf-m-inset-lg-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--lg)}.pf-c-divider.pf-m-inset-xl-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--xl)}.pf-c-divider.pf-m-inset-2xl-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--2xl)}.pf-c-divider.pf-m-inset-3xl-on-sm{--pf-c-divider--after--Inset:var(--pf-global--spacer--3xl)}}@media (min-width:768px){.pf-c-divider.pf-m-inset-none-on-md{--pf-c-divider--after--Inset:0%}.pf-c-divider.pf-m-inset-xs-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--xs)}.pf-c-divider.pf-m-inset-sm-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--sm)}.pf-c-divider.pf-m-inset-md-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--md)}.pf-c-divider.pf-m-inset-lg-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--lg)}.pf-c-divider.pf-m-inset-xl-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--xl)}.pf-c-divider.pf-m-inset-2xl-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--2xl)}.pf-c-divider.pf-m-inset-3xl-on-md{--pf-c-divider--after--Inset:var(--pf-global--spacer--3xl)}}@media (min-width:992px){.pf-c-divider.pf-m-inset-none-on-lg{--pf-c-divider--after--Inset:0%}.pf-c-divider.pf-m-inset-xs-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--xs)}.pf-c-divider.pf-m-inset-sm-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--sm)}.pf-c-divider.pf-m-inset-md-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--md)}.pf-c-divider.pf-m-inset-lg-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--lg)}.pf-c-divider.pf-m-inset-xl-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--xl)}.pf-c-divider.pf-m-inset-2xl-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--2xl)}.pf-c-divider.pf-m-inset-3xl-on-lg{--pf-c-divider--after--Inset:var(--pf-global--spacer--3xl)}}@media (min-width:1200px){.pf-c-divider.pf-m-inset-none-on-xl{--pf-c-divider--after--Inset:0%}.pf-c-divider.pf-m-inset-xs-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--xs)}.pf-c-divider.pf-m-inset-sm-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--sm)}.pf-c-divider.pf-m-inset-md-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--md)}.pf-c-divider.pf-m-inset-lg-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--lg)}.pf-c-divider.pf-m-inset-xl-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--xl)}.pf-c-divider.pf-m-inset-2xl-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--2xl)}.pf-c-divider.pf-m-inset-3xl-on-xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--3xl)}}@media (min-width:1450px){.pf-c-divider.pf-m-inset-none-on-2xl{--pf-c-divider--after--Inset:0%}.pf-c-divider.pf-m-inset-xs-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--xs)}.pf-c-divider.pf-m-inset-sm-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--sm)}.pf-c-divider.pf-m-inset-md-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--md)}.pf-c-divider.pf-m-inset-lg-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--lg)}.pf-c-divider.pf-m-inset-xl-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--xl)}.pf-c-divider.pf-m-inset-2xl-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--2xl)}.pf-c-divider.pf-m-inset-3xl-on-2xl{--pf-c-divider--after--Inset:var(--pf-global--spacer--3xl)}}.pf-c-drawer{--pf-c-drawer__section--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-drawer__content--FlexBasis:100%;--pf-c-drawer__content--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-drawer__content--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-drawer__panel--FlexBasis:100%;--pf-c-drawer__panel--md--FlexBasis:50%;--pf-c-drawer__panel--MinWidth:50%;--pf-c-drawer__panel--MaxHeight:auto;--pf-c-drawer--m-panel-bottom__panel--md--MinHeight:50%;--pf-c-drawer__panel--xl--MinWidth:28.125rem;--pf-c-drawer__panel--xl--FlexBasis:28.125rem;--pf-c-drawer--m-panel-bottom__panel--xl--MinHeight:18.75rem;--pf-c-drawer--m-panel-bottom__panel--xl--FlexBasis:18.75rem;--pf-c-drawer__panel--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-drawer__panel--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-drawer__panel--TransitionDuration:var(--pf-global--TransitionDuration);--pf-c-drawer__panel--TransitionProperty:margin,transform,box-shadow,flex-basis;--pf-c-drawer__panel--m-resizable--PaddingLeft:var(--pf-c-drawer__splitter--m-vertical--Width);--pf-c-drawer--m-panel-left__panel--m-resizable--PaddingRight:var(--pf-c-drawer__splitter--m-vertical--Width);--pf-c-drawer--m-panel-bottom__panel--m-resizable--PaddingTop:var(--pf-c-drawer__splitter--Height);--pf-c-drawer--child--PaddingTop:var(--pf-global--spacer--md);--pf-c-drawer--child--PaddingRight:var(--pf-global--spacer--md);--pf-c-drawer--child--PaddingBottom:var(--pf-global--spacer--md);--pf-c-drawer--child--PaddingLeft:var(--pf-global--spacer--md);--pf-c-drawer--child--md--PaddingTop:var(--pf-global--spacer--lg);--pf-c-drawer--child--md--PaddingRight:var(--pf-global--spacer--lg);--pf-c-drawer--child--md--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-drawer--child--md--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-drawer--child--m-padding--PaddingTop:var(--pf-global--spacer--md);--pf-c-drawer--child--m-padding--PaddingRight:var(--pf-global--spacer--md);--pf-c-drawer--child--m-padding--PaddingBottom:var(--pf-global--spacer--md);--pf-c-drawer--child--m-padding--PaddingLeft:var(--pf-global--spacer--md);--pf-c-drawer--child--m-padding--md--PaddingTop:var(--pf-global--spacer--lg);--pf-c-drawer--child--m-padding--md--PaddingRight:var(--pf-global--spacer--lg);--pf-c-drawer--child--m-padding--md--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-drawer--child--m-padding--md--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-drawer__content--child--PaddingTop:0;--pf-c-drawer__content--child--PaddingRight:0;--pf-c-drawer__content--child--PaddingBottom:0;--pf-c-drawer__content--child--PaddingLeft:0;--pf-c-drawer__splitter--Top:0;--pf-c-drawer__splitter--Right:auto;--pf-c-drawer__splitter--Bottom:0;--pf-c-drawer__splitter--Left:0;--pf-c-drawer__splitter--Height:0.5625rem;--pf-c-drawer__splitter--Width:100%;--pf-c-drawer__splitter--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-drawer__splitter--Cursor:row-resize;--pf-c-drawer__splitter--m-vertical--Height:100%;--pf-c-drawer__splitter--m-vertical--Width:0.5625rem;--pf-c-drawer__splitter--m-vertical--Cursor:col-resize;--pf-c-drawer--m-inline__splitter--focus--OutlineOffset:-0.0625rem;--pf-c-drawer__splitter--after--BorderColor:var(--pf-global--BorderColor--100);--pf-c-drawer__splitter--after--border-width--base:var(--pf-global--BorderWidth--sm);--pf-c-drawer__splitter--after--BorderTopWidth:0;--pf-c-drawer__splitter--after--BorderRightWidth:var(--pf-c-drawer__splitter--after--border-width--base);--pf-c-drawer__splitter--after--BorderBottomWidth:0;--pf-c-drawer__splitter--after--BorderLeftWidth:0;--pf-c-drawer--m-panel-left__splitter--after--BorderLeftWidth:var(--pf-c-drawer__splitter--after--border-width--base);--pf-c-drawer--m-panel-bottom__splitter--after--BorderBottomWidth:var(--pf-c-drawer__splitter--after--border-width--base);--pf-c-drawer--m-inline__splitter--m-vertical--Width:0.625rem;--pf-c-drawer--m-inline__splitter-handle--Left:50%;--pf-c-drawer--m-inline__splitter--after--BorderRightWidth:var(--pf-c-drawer__splitter--after--border-width--base);--pf-c-drawer--m-inline__splitter--after--BorderLeftWidth:var(--pf-c-drawer__splitter--after--border-width--base);--pf-c-drawer--m-inline--m-panel-bottom__splitter--Height:0.625rem;--pf-c-drawer--m-inline--m-panel-bottom__splitter-handle--Top:50%;--pf-c-drawer--m-inline--m-panel-bottom__splitter--after--BorderTopWidth:var(--pf-c-drawer__splitter--after--border-width--base);--pf-c-drawer__splitter-handle--Top:50%;--pf-c-drawer__splitter-handle--Left:calc(50% - var(--pf-c-drawer__splitter--after--border-width--base));--pf-c-drawer--m-panel-left__splitter-handle--Left:50%;--pf-c-drawer--m-panel-bottom__splitter-handle--Top:calc(50% - var(--pf-c-drawer__splitter--after--border-width--base));--pf-c-drawer__splitter-handle--after--BorderColor:var(--pf-global--Color--200);--pf-c-drawer__splitter-handle--after--BorderTopWidth:var(--pf-global--BorderWidth--sm);--pf-c-drawer__splitter-handle--after--BorderRightWidth:0;--pf-c-drawer__splitter-handle--after--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-drawer__splitter-handle--after--BorderLeftWidth:0;--pf-c-drawer__splitter--hover__splitter-handle--after--BorderColor:var(--pf-global--Color--100);--pf-c-drawer__splitter--focus__splitter-handle--after--BorderColor:var(--pf-global--Color--100);--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderTopWidth:0;--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderRightWidth:var(--pf-global--BorderWidth--sm);--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderBottomWidth:0;--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderLeftWidth:var(--pf-global--BorderWidth--sm);--pf-c-drawer__splitter-handle--after--Width:0.75rem;--pf-c-drawer__splitter-handle--after--Height:0.25rem;--pf-c-drawer__splitter--m-vertical__splitter-handle--after--Width:0.25rem;--pf-c-drawer__splitter--m-vertical__splitter-handle--after--Height:0.75rem;--pf-c-drawer__actions--MarginTop:calc(var(pf-global--spacer--form-element)*-1);--pf-c-drawer__actions--MarginRight:calc(var(pf-global--spacer--form-element)*-1);--pf-c-drawer__panel--BoxShadow:none;--pf-c-drawer--m-expanded__panel--BoxShadow:var(--pf-global--BoxShadow--lg-left);--pf-c-drawer--m-expanded--m-panel-left__panel--BoxShadow:var(--pf-global--BoxShadow--lg-right);--pf-c-drawer--m-expanded--m-panel-bottom__panel--BoxShadow:var(--pf-global--BoxShadow--lg-top);--pf-c-drawer__panel--after--Width:var(--pf-global--BorderWidth--sm);--pf-c-drawer--m-panel-bottom__panel--after--Height:var(--pf-global--BorderWidth--sm);--pf-c-drawer__panel--after--BackgroundColor:transparent;--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor:var(--pf-global--BorderColor--100);--pf-c-drawer--m-inline__panel--PaddingLeft:var(--pf-c-drawer__panel--after--Width);--pf-c-drawer--m-panel-left--m-inline__panel--PaddingRight:var(--pf-c-drawer__panel--after--Width);--pf-c-drawer--m-panel-bottom--m-inline__panel--PaddingTop:var(--pf-c-drawer__panel--after--Width);display:flex;flex-direction:column;height:100%;overflow-x:hidden}@media screen and (min-width:768px){.pf-c-drawer{--pf-c-drawer__panel--FlexBasis:var(--pf-c-drawer__panel--md--FlexBasis);--pf-c-drawer--child--PaddingTop:var(--pf-c-drawer--child--md--PaddingTop);--pf-c-drawer--child--PaddingRight:var(--pf-c-drawer--child--md--PaddingRight);--pf-c-drawer--child--PaddingBottom:var(--pf-c-drawer--child--md--PaddingBottom);--pf-c-drawer--child--PaddingLeft:var(--pf-c-drawer--child--md--PaddingLeft);--pf-c-drawer--child--m-padding--PaddingTop:var(--pf-c-drawer--child--m-padding--md--PaddingTop);--pf-c-drawer--child--m-padding--PaddingRight:var(--pf-c-drawer--child--m-padding--md--PaddingRight);--pf-c-drawer--child--m-padding--PaddingBottom:var(--pf-c-drawer--child--m-padding--md--PaddingBottom);--pf-c-drawer--child--m-padding--PaddingLeft:var(--pf-c-drawer--child--m-padding--md--PaddingLeft)}}@media screen and (min-width:1200px){.pf-c-drawer{--pf-c-drawer__panel--FlexBasis:var(--pf-c-drawer__panel--xl--FlexBasis);--pf-c-drawer__panel--MinWidth:var(--pf-c-drawer__panel--xl--MinWidth)}.pf-c-drawer.pf-m-panel-bottom{--pf-c-drawer__panel--MinWidth:auto;--pf-c-drawer__panel--FlexBasis:var(--pf-c-drawer--m-panel-bottom__panel--xl--FlexBasis);--pf-c-drawer__panel--MinHeight:var(--pf-c-drawer--m-panel-bottom__panel--xl--MinHeight)}}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel{padding-left:var(--pf-c-drawer--m-inline__panel--PaddingLeft)}.pf-c-drawer.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{order:0;margin-right:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);transform:translateX(-100%)}.pf-c-drawer.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__content{order:1}.pf-c-drawer.pf-m-panel-bottom>.pf-c-drawer__main{flex-direction:column}.pf-c-drawer.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(-100%)}.pf-c-drawer.pf-m-expanded.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-expanded.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateY(-100%)}.pf-c-drawer__section{flex-grow:0;background-color:var(--pf-c-drawer__section--BackgroundColor)}.pf-c-drawer__section.pf-m-no-background{background-color:transparent}.pf-c-drawer__main{display:flex;flex-grow:1;overflow:hidden}.pf-c-drawer__content,.pf-c-drawer__panel{display:flex;flex-direction:column;flex-shrink:0;overflow:auto}.pf-c-drawer__content{z-index:var(--pf-c-drawer__content--ZIndex);flex-basis:var(--pf-c-drawer__content--FlexBasis);order:0;background-color:var(--pf-c-drawer__content--BackgroundColor)}.pf-c-drawer__content>.pf-c-drawer__body{padding:var(--pf-c-drawer__content--child--PaddingTop) var(--pf-c-drawer__content--child--PaddingRight) var(--pf-c-drawer__content--child--PaddingBottom) var(--pf-c-drawer__content--child--PaddingLeft)}.pf-c-drawer__content.pf-m-no-background{background-color:transparent}.pf-c-drawer__panel{position:relative;z-index:var(--pf-c-drawer__panel--ZIndex);flex-basis:var(--pf-c-drawer__panel--FlexBasis);order:1;max-height:var(--pf-c-drawer__panel--MaxHeight);overflow:auto;background-color:var(--pf-c-drawer__panel--BackgroundColor);box-shadow:var(--pf-c-drawer__panel--BoxShadow);transition-duration:var(--pf-c-drawer__panel--TransitionDuration);transition-property:var(--pf-c-drawer__panel--TransitionProperty);-webkit-overflow-scrolling:touch}.pf-c-drawer__panel:after{position:absolute;top:0;left:0;width:var(--pf-c-drawer__panel--after--Width);height:100%;content:"";background-color:var(--pf-c-drawer__panel--after--BackgroundColor)}.pf-c-drawer__panel.pf-m-no-background{background-color:transparent}@keyframes pf-remove-tab-focus{to{visibility:hidden}}.pf-c-drawer__panel[hidden]{animation-name:pf-remove-tab-focus;animation-delay:var(--pf-c-drawer__panel--TransitionDuration);animation-fill-mode:forwards}.pf-c-drawer__head{display:grid;grid-template-columns:auto;grid-auto-columns:max-content}.pf-c-drawer__head>*{grid-column:1}.pf-c-drawer__actions{grid-column:2;grid-row:1;display:flex;align-self:baseline;margin-top:var(--pf-c-drawer__actions--MarginTop);margin-right:var(--pf-c-drawer__actions--MarginRight)}.pf-c-drawer__body{min-height:0;padding:var(--pf-c-drawer--child--PaddingTop) var(--pf-c-drawer--child--PaddingRight) var(--pf-c-drawer--child--PaddingBottom) var(--pf-c-drawer--child--PaddingLeft)}.pf-c-drawer__body.pf-m-no-padding{padding:0}.pf-c-drawer__body.pf-m-no-padding>.pf-c-drawer__actions,.pf-c-drawer__body.pf-m-no-padding>.pf-c-drawer__head>.pf-c-drawer__actions{margin-top:0;margin-right:0}.pf-c-drawer__body.pf-m-padding{padding:var(--pf-c-drawer--child--m-padding--PaddingTop) var(--pf-c-drawer--child--m-padding--PaddingRight) var(--pf-c-drawer--child--m-padding--PaddingBottom) var(--pf-c-drawer--child--m-padding--PaddingLeft)}.pf-c-drawer__body:not(.pf-m-no-padding)+*{padding-top:0}.pf-c-drawer__body:last-child{flex:1 1}.pf-c-drawer__body>.pf-c-page__main{min-height:100%}.pf-c-drawer__splitter{position:absolute;top:var(--pf-c-drawer__splitter--Top);right:var(--pf-c-drawer__splitter--Right);bottom:var(--pf-c-drawer__splitter--Bottom);left:var(--pf-c-drawer__splitter--Left);display:none;width:var(--pf-c-drawer__splitter--Width);height:var(--pf-c-drawer__splitter--Height);cursor:var(--pf-c-drawer__splitter--Cursor);visibility:hidden;background-color:var(--pf-c-drawer__splitter--BackgroundColor)}.pf-c-drawer__splitter.pf-m-vertical{--pf-c-drawer__splitter--Height:var(--pf-c-drawer__splitter--m-vertical--Height);--pf-c-drawer__splitter--Width:var(--pf-c-drawer__splitter--m-vertical--Width);--pf-c-drawer__splitter--Cursor:var(--pf-c-drawer__splitter--m-vertical--Cursor);--pf-c-drawer__splitter-handle--after--Width:var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--Width);--pf-c-drawer__splitter-handle--after--Height:var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--Height);--pf-c-drawer__splitter-handle--after--BorderTopWidth:var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderTopWidth);--pf-c-drawer__splitter-handle--after--BorderRightWidth:var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderRightWidth);--pf-c-drawer__splitter-handle--after--BorderBottomWidth:var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderBottomWidth);--pf-c-drawer__splitter-handle--after--BorderLeftWidth:var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderLeftWidth)}.pf-c-drawer__splitter:hover{--pf-c-drawer__splitter-handle--after--BorderColor:var(--pf-c-drawer__splitter--hover__splitter-handle--after--BorderColor)}.pf-c-drawer__splitter:focus{--pf-c-drawer__splitter-handle--after--BorderColor:var(--pf-c-drawer__splitter--focus__splitter-handle--after--BorderColor)}.pf-c-drawer__splitter:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:solid var(--pf-c-drawer__splitter--after--BorderColor);border-width:var(--pf-c-drawer__splitter--after--BorderTopWidth) var(--pf-c-drawer__splitter--after--BorderRightWidth) var(--pf-c-drawer__splitter--after--BorderBottomWidth) var(--pf-c-drawer__splitter--after--BorderLeftWidth)}.pf-c-drawer__splitter-handle{position:absolute;top:var(--pf-c-drawer__splitter-handle--Top);left:var(--pf-c-drawer__splitter-handle--Left);transform:translate(-50%,-50%)}.pf-c-drawer__splitter-handle:after{display:block;width:var(--pf-c-drawer__splitter-handle--after--Width);height:var(--pf-c-drawer__splitter-handle--after--Height);content:"";border-left:var(--pf-c-drawer__splitter-handle--after--BorderLeftWidth) solid var(--pf-c-drawer__splitter-handle--after--BorderColor);border-bottom:var(--pf-c-drawer__splitter-handle--after--BorderBottomWidth) solid var(--pf-c-drawer__splitter-handle--after--BorderColor);border-right:var(--pf-c-drawer__splitter-handle--after--BorderRightWidth) solid var(--pf-c-drawer__splitter-handle--after--BorderColor);border-top:var(--pf-c-drawer__splitter-handle--after--BorderTopWidth) solid var(--pf-c-drawer__splitter-handle--after--BorderColor)}@media screen and (min-width:768px){.pf-c-drawer{min-width:var(--pf-c-drawer__panel--MinWidth)}.pf-c-drawer.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{box-shadow:var(--pf-c-drawer--m-expanded__panel--BoxShadow)}.pf-c-drawer>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable{padding-left:var(--pf-c-drawer__panel--m-resizable--PaddingLeft)}.pf-c-drawer>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable:after{width:0;height:0}.pf-c-drawer.pf-m-panel-left{--pf-c-drawer--m-expanded__panel--BoxShadow:var(--pf-c-drawer--m-expanded--m-panel-left__panel--BoxShadow)}.pf-c-drawer.pf-m-panel-left.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-panel-left.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel{padding-right:var(--pf-c-drawer--m-panel-left--m-inline__panel--PaddingRight);padding-left:0}.pf-c-drawer.pf-m-panel-left.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel:after{right:0;left:auto}.pf-c-drawer.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable{padding-right:var(--pf-c-drawer--m-panel-left__panel--m-resizable--PaddingRight);padding-left:0}.pf-c-drawer.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable>.pf-c-drawer__splitter{--pf-c-drawer__splitter--Right:0;--pf-c-drawer__splitter--Left:auto;--pf-c-drawer__splitter-handle--Left:var(--pf-c-drawer--m-panel-left__splitter-handle--Left);--pf-c-drawer__splitter--after--BorderRightWidth:0;--pf-c-drawer__splitter--after--BorderLeftWidth:var(--pf-c-drawer--m-panel-left__splitter--after--BorderLeftWidth)}.pf-c-drawer.pf-m-panel-bottom{--pf-c-drawer--m-expanded__panel--BoxShadow:var(--pf-c-drawer--m-expanded--m-panel-bottom__panel--BoxShadow);--pf-c-drawer__panel--MaxHeight:100%;min-width:auto;min-height:var(--pf-c-drawer--m-panel-bottom__panel--md--MinHeight)}.pf-c-drawer.pf-m-panel-bottom.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-panel-bottom.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel{padding-top:var(--pf-c-drawer--m-panel-bottom--m-inline__panel--PaddingTop);padding-left:0}.pf-c-drawer.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel:after{top:0;left:auto;width:100%;height:var(--pf-c-drawer--m-panel-bottom__panel--after--Height)}.pf-c-drawer.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable{padding-top:var(--pf-c-drawer--m-panel-bottom__panel--m-resizable--PaddingTop);padding-left:0}.pf-c-drawer.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable>.pf-c-drawer__splitter{--pf-c-drawer__splitter--Top:0;--pf-c-drawer__splitter--Right:0;--pf-c-drawer__splitter--Bottom:auto;--pf-c-drawer__splitter-handle--Top:var(--pf-c-drawer--m-panel-bottom__splitter-handle--Top);--pf-c-drawer__splitter--after--BorderRightWidth:0;--pf-c-drawer__splitter--after--BorderBottomWidth:var(--pf-c-drawer--m-panel-bottom__splitter--after--BorderBottomWidth)}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable>.pf-c-drawer__splitter{--pf-c-drawer__splitter--m-vertical--Width:var(--pf-c-drawer--m-inline__splitter--m-vertical--Width);--pf-c-drawer__splitter-handle--Left:var(--pf-c-drawer--m-inline__splitter-handle--Left);--pf-c-drawer__splitter--after--BorderRightWidth:var(--pf-c-drawer--m-inline__splitter--after--BorderRightWidth);--pf-c-drawer__splitter--after--BorderLeftWidth:var(--pf-c-drawer--m-inline__splitter--after--BorderLeftWidth);outline-offset:var(--pf-c-drawer--m-inline__splitter--focus--OutlineOffset)}.pf-c-drawer.pf-m-inline.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-resizable>.pf-c-drawer__splitter{--pf-c-drawer__splitter--Height:var(--pf-c-drawer--m-inline--m-panel-bottom__splitter--Height);--pf-c-drawer__splitter-handle--Top:var(--pf-c-drawer--m-inline--m-panel-bottom__splitter-handle--Top);--pf-c-drawer__splitter--after--BorderTopWidth:var(--pf-c-drawer--m-inline--m-panel-bottom__splitter--after--BorderTopWidth);--pf-c-drawer__splitter--after--BorderRightWidth:0;--pf-c-drawer__splitter--after--BorderLeftWidth:0}.pf-c-drawer.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-no-border,.pf-c-drawer>.pf-c-drawer__main>.pf-c-drawer__panel.pf-m-no-border{--pf-c-drawer--m-expanded__panel--BoxShadow:none}.pf-c-drawer__splitter{display:block;visibility:visible}}@media (min-width:768px){.pf-c-drawer__panel.pf-m-width-25{--pf-c-drawer__panel--FlexBasis:25%}.pf-c-drawer__panel.pf-m-width-33{--pf-c-drawer__panel--FlexBasis:33%}.pf-c-drawer__panel.pf-m-width-50{--pf-c-drawer__panel--FlexBasis:50%}.pf-c-drawer__panel.pf-m-width-66{--pf-c-drawer__panel--FlexBasis:66%}.pf-c-drawer__panel.pf-m-width-75{--pf-c-drawer__panel--FlexBasis:75%}.pf-c-drawer__panel.pf-m-width-100{--pf-c-drawer__panel--FlexBasis:100%}}@media (min-width:992px){.pf-c-drawer__panel.pf-m-width-25-on-lg{--pf-c-drawer__panel--FlexBasis:25%}.pf-c-drawer__panel.pf-m-width-33-on-lg{--pf-c-drawer__panel--FlexBasis:33%}.pf-c-drawer__panel.pf-m-width-50-on-lg{--pf-c-drawer__panel--FlexBasis:50%}.pf-c-drawer__panel.pf-m-width-66-on-lg{--pf-c-drawer__panel--FlexBasis:66%}.pf-c-drawer__panel.pf-m-width-75-on-lg{--pf-c-drawer__panel--FlexBasis:75%}.pf-c-drawer__panel.pf-m-width-100-on-lg{--pf-c-drawer__panel--FlexBasis:100%}}@media (min-width:1200px){.pf-c-drawer__panel.pf-m-width-25-on-xl{--pf-c-drawer__panel--FlexBasis:25%}.pf-c-drawer__panel.pf-m-width-33-on-xl{--pf-c-drawer__panel--FlexBasis:33%}.pf-c-drawer__panel.pf-m-width-50-on-xl{--pf-c-drawer__panel--FlexBasis:50%}.pf-c-drawer__panel.pf-m-width-66-on-xl{--pf-c-drawer__panel--FlexBasis:66%}.pf-c-drawer__panel.pf-m-width-75-on-xl{--pf-c-drawer__panel--FlexBasis:75%}.pf-c-drawer__panel.pf-m-width-100-on-xl{--pf-c-drawer__panel--FlexBasis:100%}}@media (min-width:1450px){.pf-c-drawer__panel.pf-m-width-25-on-2xl{--pf-c-drawer__panel--FlexBasis:25%}.pf-c-drawer__panel.pf-m-width-33-on-2xl{--pf-c-drawer__panel--FlexBasis:33%}.pf-c-drawer__panel.pf-m-width-50-on-2xl{--pf-c-drawer__panel--FlexBasis:50%}.pf-c-drawer__panel.pf-m-width-66-on-2xl{--pf-c-drawer__panel--FlexBasis:66%}.pf-c-drawer__panel.pf-m-width-75-on-2xl{--pf-c-drawer__panel--FlexBasis:75%}.pf-c-drawer__panel.pf-m-width-100-on-2xl{--pf-c-drawer__panel--FlexBasis:100%}}@media (min-width:768px){.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__content,.pf-c-drawer.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__content{flex-shrink:1}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel{--pf-c-drawer--m-expanded__panel--BoxShadow:none}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after,.pf-c-drawer.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after{background-color:var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor)}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__content{overflow-x:auto}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);transform:translateX(100%)}.pf-c-drawer.pf-m-inline.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-inline.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);margin-left:0;transform:translateX(-100%)}.pf-c-drawer.pf-m-inline.pf-m-panel-left.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-static.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:none;visibility:hidden}}@media (min-width:992px){.pf-c-drawer.pf-m-inline-on-lg>.pf-c-drawer__main>.pf-c-drawer__content,.pf-c-drawer.pf-m-static-on-lg>.pf-c-drawer__main>.pf-c-drawer__content{flex-shrink:1}.pf-c-drawer.pf-m-inline-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-static-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel{--pf-c-drawer--m-expanded__panel--BoxShadow:none}.pf-c-drawer.pf-m-inline-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after,.pf-c-drawer.pf-m-static-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after{background-color:var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor)}.pf-c-drawer.pf-m-inline-on-lg>.pf-c-drawer__main>.pf-c-drawer__content{overflow-x:auto}.pf-c-drawer.pf-m-inline-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);transform:translateX(100%)}.pf-c-drawer.pf-m-inline-on-lg.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-inline-on-lg.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);margin-left:0;transform:translateX(-100%)}.pf-c-drawer.pf-m-inline-on-lg.pf-m-panel-left.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline-on-lg.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-static-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static-on-lg.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-static-on-lg.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static-on-lg>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:none;visibility:hidden}}@media (min-width:1200px){.pf-c-drawer.pf-m-inline-on-xl>.pf-c-drawer__main>.pf-c-drawer__content,.pf-c-drawer.pf-m-static-on-xl>.pf-c-drawer__main>.pf-c-drawer__content{flex-shrink:1}.pf-c-drawer.pf-m-inline-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-static-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel{--pf-c-drawer--m-expanded__panel--BoxShadow:none}.pf-c-drawer.pf-m-inline-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after,.pf-c-drawer.pf-m-static-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after{background-color:var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor)}.pf-c-drawer.pf-m-inline-on-xl>.pf-c-drawer__main>.pf-c-drawer__content{overflow-x:auto}.pf-c-drawer.pf-m-inline-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);transform:translateX(100%)}.pf-c-drawer.pf-m-inline-on-xl.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-inline-on-xl.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);margin-left:0;transform:translateX(-100%)}.pf-c-drawer.pf-m-inline-on-xl.pf-m-panel-left.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline-on-xl.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-static-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static-on-xl.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-static-on-xl.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static-on-xl>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:none;visibility:hidden}}@media (min-width:1450px){.pf-c-drawer.pf-m-inline-on-2xl>.pf-c-drawer__main>.pf-c-drawer__content,.pf-c-drawer.pf-m-static-on-2xl>.pf-c-drawer__main>.pf-c-drawer__content{flex-shrink:1}.pf-c-drawer.pf-m-inline-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel,.pf-c-drawer.pf-m-static-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel{--pf-c-drawer--m-expanded__panel--BoxShadow:none}.pf-c-drawer.pf-m-inline-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after,.pf-c-drawer.pf-m-static-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel:not(.pf-m-no-border):after{background-color:var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor)}.pf-c-drawer.pf-m-inline-on-2xl>.pf-c-drawer__main>.pf-c-drawer__content{overflow-x:auto}.pf-c-drawer.pf-m-inline-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);transform:translateX(100%)}.pf-c-drawer.pf-m-inline-on-2xl.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-left:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-inline-on-2xl.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:calc(var(--pf-c-drawer__panel--FlexBasis)*-1);margin-left:0;transform:translateX(-100%)}.pf-c-drawer.pf-m-inline-on-2xl.pf-m-panel-left.pf-m-expanded>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-inline-on-2xl.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:unset;visibility:visible}.pf-c-drawer.pf-m-static-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static-on-2xl.pf-m-panel-left>.pf-c-drawer__main>.pf-c-drawer__panel{margin-right:0;transform:translateX(0)}.pf-c-drawer.pf-m-static-on-2xl.pf-m-panel-bottom>.pf-c-drawer__main>.pf-c-drawer__panel{transform:translateX(0)}.pf-c-drawer.pf-m-static-on-2xl>.pf-c-drawer__main>.pf-c-drawer__panel>.pf-c-drawer__body>.pf-c-drawer__head .pf-c-drawer__close{display:none;visibility:hidden}}.pf-c-dropdown{--pf-c-dropdown__toggle--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-dropdown__toggle--PaddingRight:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-dropdown__toggle--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle--MinWidth:var(--pf-global--target-size--MinWidth);--pf-c-dropdown__toggle--FontSize:var(--pf-global--FontSize--md);--pf-c-dropdown__toggle--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-dropdown__toggle--Color:var(--pf-global--Color--100);--pf-c-dropdown__toggle--LineHeight:var(--pf-global--LineHeight--md);--pf-c-dropdown__toggle--BackgroundColor:transparent;--pf-c-dropdown__toggle--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-dropdown__toggle--before--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-dropdown__toggle--before--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-dropdown__toggle--before--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-dropdown__toggle--before--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-dropdown__toggle--hover--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-dropdown__toggle--active--before--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-dropdown__toggle--active--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-dropdown__toggle--focus--before--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-dropdown__toggle--focus--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-dropdown--m-expanded__toggle--before--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-dropdown--m-expanded__toggle--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-dropdown__toggle--disabled--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-dropdown__toggle--m-plain--Color:var(--pf-global--Color--200);--pf-c-dropdown__toggle--m-plain--hover--Color:var(--pf-global--Color--100);--pf-c-dropdown__toggle--m-plain--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-dropdown__toggle--m-plain--child--LineHeight:normal;--pf-c-dropdown__toggle--m-primary--Color:var(--pf-global--Color--light-100);--pf-c-dropdown__toggle--m-primary--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-dropdown__toggle--m-primary--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-dropdown__toggle--m-primary--hover--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-dropdown__toggle--m-primary--active--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-dropdown__toggle--m-primary--focus--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-dropdown--m-expanded__toggle--m-primary--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-dropdown__toggle-button--Color:var(--pf-global--Color--100);--pf-c-dropdown__toggle--m-split-button--child--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-dropdown__toggle--m-split-button--child--PaddingRight:var(--pf-global--spacer--xs);--pf-c-dropdown__toggle--m-split-button--child--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-dropdown__toggle--m-split-button--child--PaddingLeft:var(--pf-global--spacer--xs);--pf-c-dropdown__toggle--m-split-button--child--BackgroundColor:transparent;--pf-c-dropdown__toggle--m-split-button--first-child--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle--m-split-button--last-child--PaddingRight:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingRight:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle--m-split-button--m-action__toggle-button--MarginRight:calc(-1*var(--pf-global--BorderWidth--sm));--pf-c-dropdown__toggle--m-split-button__toggle-check__input--TranslateY:-0.0625rem;--pf-c-dropdown__toggle--m-split-button__toggle-text--MarginLeft:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle-icon--LineHeight:var(--pf-global--LineHeight--md);--pf-c-dropdown__toggle-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-dropdown__toggle-icon--MarginLeft:var(--pf-global--spacer--md);--pf-c-dropdown--m-top--m-expanded__toggle-icon--Rotate:180deg;--pf-c-dropdown__menu--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-dropdown__menu--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-dropdown__menu--PaddingTop:var(--pf-global--spacer--sm);--pf-c-dropdown__menu--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-dropdown__menu--Top:calc(100% + var(--pf-global--spacer--xs));--pf-c-dropdown__menu--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-dropdown--m-top__menu--Top:0;--pf-c-dropdown--m-top__menu--TranslateY:calc(-100% - var(--pf-global--spacer--xs));--pf-c-dropdown__menu-item--BackgroundColor:transparent;--pf-c-dropdown__menu-item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-dropdown__menu-item--PaddingRight:var(--pf-global--spacer--md);--pf-c-dropdown__menu-item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-dropdown__menu-item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-dropdown__menu-item--FontSize:var(--pf-global--FontSize--md);--pf-c-dropdown__menu-item--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-dropdown__menu-item--LineHeight:var(--pf-global--LineHeight--md);--pf-c-dropdown__menu-item--Color:var(--pf-global--Color--dark-100);--pf-c-dropdown__menu-item--hover--Color:var(--pf-global--Color--dark-100);--pf-c-dropdown__menu-item--disabled--Color:var(--pf-global--Color--dark-200);--pf-c-dropdown__menu-item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-dropdown__menu-item--disabled--BackgroundColor:transparent;--pf-c-dropdown__menu-item--m-text--Color:var(--pf-global--Color--dark-200);--pf-c-dropdown__menu-item-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-dropdown__menu-item-icon--Width:var(--pf-global--icon--FontSize--lg);--pf-c-dropdown__menu-item-icon--Height:var(--pf-global--icon--FontSize--lg);--pf-c-dropdown__menu-item-description--FontSize:var(--pf-global--FontSize--xs);--pf-c-dropdown__menu-item-description--Color:var(--pf-global--Color--dark-200);--pf-c-dropdown__group--group--PaddingTop:var(--pf-global--spacer--sm);--pf-c-dropdown__group-title--PaddingTop:var(--pf-global--spacer--sm);--pf-c-dropdown__group-title--PaddingRight:var(--pf-c-dropdown__menu-item--PaddingRight);--pf-c-dropdown__group-title--PaddingBottom:var(--pf-c-dropdown__menu-item--PaddingBottom);--pf-c-dropdown__group-title--PaddingLeft:var(--pf-c-dropdown__menu-item--PaddingLeft);--pf-c-dropdown__group-title--FontSize:var(--pf-global--FontSize--sm);--pf-c-dropdown__group-title--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-dropdown__group-title--Color:var(--pf-global--Color--dark-200);--pf-c-dropdown__toggle-image--MarginTop:var(--pf-global--spacer--xs);--pf-c-dropdown__toggle-image--MarginBottom:var(--pf-global--spacer--xs);--pf-c-dropdown__toggle-image--MarginRight:var(--pf-global--spacer--sm);--pf-c-dropdown--c-divider--MarginTop:var(--pf-global--spacer--sm);--pf-c-dropdown--c-divider--MarginBottom:var(--pf-global--spacer--sm);position:relative;display:inline-block;max-width:100%}.pf-c-dropdown .pf-c-divider{margin-top:var(--pf-c-dropdown--c-divider--MarginTop);margin-bottom:var(--pf-c-dropdown--c-divider--MarginBottom)}.pf-c-dropdown .pf-c-divider:last-child{--pf-c-dropdown--c-divider--MarginBottom:0}.pf-c-dropdown__toggle{position:relative;display:flex;align-items:center;justify-content:space-between;min-width:var(--pf-c-dropdown__toggle--MinWidth);max-width:100%;padding:var(--pf-c-dropdown__toggle--PaddingTop) var(--pf-c-dropdown__toggle--PaddingRight) var(--pf-c-dropdown__toggle--PaddingBottom) var(--pf-c-dropdown__toggle--PaddingLeft);font-size:var(--pf-c-dropdown__toggle--FontSize);font-weight:var(--pf-c-dropdown__toggle--FontWeight);line-height:var(--pf-c-dropdown__toggle--LineHeight);color:var(--pf-c-dropdown__toggle--Color);background-color:var(--pf-c-dropdown__toggle--BackgroundColor);border:none}.pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:before,.pf-c-dropdown__toggle:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-dropdown__toggle--before--BorderWidth) solid;border-color:var(--pf-c-dropdown__toggle--before--BorderTopColor) var(--pf-c-dropdown__toggle--before--BorderRightColor) var(--pf-c-dropdown__toggle--before--BorderBottomColor) var(--pf-c-dropdown__toggle--before--BorderLeftColor)}.pf-c-dropdown__toggle.pf-m-disabled,.pf-c-dropdown__toggle:disabled{pointer-events:none}.pf-c-dropdown__toggle.pf-m-disabled:not(.pf-m-plain),.pf-c-dropdown__toggle:disabled:not(.pf-m-plain){--pf-c-dropdown__toggle--BackgroundColor:var(--pf-c-dropdown__toggle--disabled--BackgroundColor)}.pf-c-dropdown__toggle.pf-m-disabled:not(.pf-m-plain):before,.pf-c-dropdown__toggle:disabled:not(.pf-m-plain):before{border:0}.pf-c-dropdown__toggle.pf-m-split-button{padding:0}.pf-c-dropdown__toggle.pf-m-split-button>*{position:relative;padding:var(--pf-c-dropdown__toggle--m-split-button--child--PaddingTop) var(--pf-c-dropdown__toggle--m-split-button--child--PaddingRight) var(--pf-c-dropdown__toggle--m-split-button--child--PaddingBottom) var(--pf-c-dropdown__toggle--m-split-button--child--PaddingLeft);background-color:var(--pf-c-dropdown__toggle--m-split-button--child--BackgroundColor)}.pf-c-dropdown__toggle.pf-m-split-button>:first-child{--pf-c-dropdown__toggle--m-split-button--child--PaddingLeft:var(--pf-c-dropdown__toggle--m-split-button--first-child--PaddingLeft)}.pf-c-dropdown__toggle.pf-m-split-button>:last-child{--pf-c-dropdown__toggle--m-split-button--child--PaddingRight:var(--pf-c-dropdown__toggle--m-split-button--last-child--PaddingRight)}.pf-c-dropdown__toggle.pf-m-split-button.pf-m-action{--pf-c-dropdown__toggle--m-split-button--child--PaddingRight:var(--pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingRight);--pf-c-dropdown__toggle--m-split-button--child--PaddingLeft:var(--pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingLeft)}.pf-c-dropdown__toggle.pf-m-split-button.pf-m-action .pf-c-dropdown__toggle-button{margin-right:var(--pf-c-dropdown__toggle--m-split-button--m-action__toggle-button--MarginRight)}.pf-c-dropdown__toggle.pf-m-split-button.pf-m-action .pf-c-dropdown__toggle-button:before{border-left:0}.pf-c-dropdown__toggle.pf-m-split-button.pf-m-action .pf-c-dropdown__toggle-button:last-child{--pf-c-dropdown__toggle--m-split-button--m-action__toggle-button--MarginRight:0}.pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-check{display:flex;align-items:center;cursor:pointer}.pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-check input{transform:translateY(var(--pf-c-dropdown__toggle--m-split-button__toggle-check__input--TranslateY))}.pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-button{color:var(--pf-c-dropdown__toggle-button--Color);border:0}.pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-text{margin-left:var(--pf-c-dropdown__toggle--m-split-button__toggle-text--MarginLeft)}.pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:hover:before,.pf-c-dropdown__toggle:not(.pf-m-action):hover:before{--pf-c-dropdown__toggle--before--BorderBottomColor:var(--pf-c-dropdown__toggle--hover--before--BorderBottomColor)}.pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:active:before,.pf-c-dropdown__toggle:not(.pf-m-action).pf-m-active:before,.pf-c-dropdown__toggle:not(.pf-m-action):active:before{--pf-c-dropdown__toggle--before--BorderBottomColor:var(--pf-c-dropdown__toggle--active--before--BorderBottomColor);border-bottom-width:var(--pf-c-dropdown__toggle--active--before--BorderBottomWidth)}.pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:focus:before,.pf-c-dropdown__toggle:not(.pf-m-action):focus:before{--pf-c-dropdown__toggle--before--BorderBottomColor:var(--pf-c-dropdown__toggle--focus--before--BorderBottomColor);border-bottom-width:var(--pf-c-dropdown__toggle--focus--before--BorderBottomWidth)}.pf-m-expanded>.pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:before,.pf-m-expanded>.pf-c-dropdown__toggle:not(.pf-m-action):before{--pf-c-dropdown__toggle--before--BorderBottomColor:var(--pf-c-dropdown--m-expanded__toggle--before--BorderBottomColor);border-bottom-width:var(--pf-c-dropdown--m-expanded__toggle--before--BorderBottomWidth)}.pf-c-dropdown__toggle.pf-m-plain{display:inline-block;color:var(--pf-c-dropdown__toggle--m-plain--Color)}.pf-c-dropdown__toggle.pf-m-plain>*{line-height:var(--pf-c-dropdown__toggle--m-plain--child--LineHeight)}.pf-c-dropdown__toggle.pf-m-plain:before{border:0}.pf-c-dropdown__toggle.pf-m-plain.pf-m-active,.pf-c-dropdown__toggle.pf-m-plain:active,.pf-c-dropdown__toggle.pf-m-plain:focus,.pf-c-dropdown__toggle.pf-m-plain:hover,.pf-m-expanded>.pf-c-dropdown__toggle.pf-m-plain{--pf-c-dropdown__toggle--m-plain--Color:var(--pf-c-dropdown__toggle--m-plain--hover--Color)}.pf-c-dropdown__toggle.pf-m-plain.pf-m-disabled,.pf-c-dropdown__toggle.pf-m-plain:disabled{--pf-c-dropdown__toggle--m-plain--Color:var(--pf-c-dropdown__toggle--m-plain--disabled--Color)}.pf-c-dropdown__toggle.pf-m-primary{--pf-c-dropdown__toggle--Color:var(--pf-c-dropdown__toggle--m-primary--Color);--pf-c-dropdown__toggle--BackgroundColor:var(--pf-c-dropdown__toggle--m-primary--BackgroundColor);border-radius:var(--pf-c-dropdown__toggle--m-primary--BorderRadius)}.pf-c-dropdown__toggle.pf-m-primary:before{border:0}.pf-c-dropdown__toggle.pf-m-primary:hover{--pf-c-dropdown__toggle--BackgroundColor:var(--pf-c-dropdown__toggle--m-primary--hover--BackgroundColor)}.pf-c-dropdown__toggle.pf-m-primary.pf-m-active,.pf-c-dropdown__toggle.pf-m-primary:active{--pf-c-dropdown__toggle--BackgroundColor:var(--pf-c-dropdown__toggle--m-primary--active--BackgroundColor)}.pf-c-dropdown__toggle.pf-m-primary:focus{--pf-c-dropdown__toggle--BackgroundColor:var(--pf-c-dropdown__toggle--m-primary--focus--BackgroundColor)}.pf-m-expanded>.pf-c-dropdown__toggle.pf-m-primary{--pf-c-dropdown__toggle--BackgroundColor:var(--pf-c-dropdown--m-expanded__toggle--m-primary--BackgroundColor)}.pf-c-dropdown__toggle .pf-c-dropdown__toggle-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-c-dropdown__toggle-icon{margin-right:var(--pf-c-dropdown__toggle-icon--MarginRight);margin-left:var(--pf-c-dropdown__toggle-icon--MarginLeft);line-height:var(--pf-c-dropdown__toggle-icon--LineHeight)}.pf-c-dropdown.pf-m-top.pf-m-expanded .pf-c-dropdown__toggle-icon{transform:rotate(var(--pf-c-dropdown--m-top--m-expanded__toggle-icon--Rotate))}.pf-c-dropdown__toggle-image{display:inline-flex;margin-top:var(--pf-c-dropdown__toggle-image--MarginTop);margin-right:var(--pf-c-dropdown__toggle-image--MarginRight);margin-bottom:var(--pf-c-dropdown__toggle-image--MarginBottom)}.pf-c-dropdown__toggle-image:last-child{--pf-c-dropdown__toggle-image--MarginRight:0}.pf-c-dropdown__menu{position:absolute;top:var(--pf-c-dropdown__menu--Top);z-index:var(--pf-c-dropdown__menu--ZIndex);min-width:100%;padding-top:var(--pf-c-dropdown__menu--PaddingTop);padding-bottom:var(--pf-c-dropdown__menu--PaddingBottom);background:var(--pf-c-dropdown__menu--BackgroundColor);background-clip:padding-box;box-shadow:var(--pf-c-dropdown__menu--BoxShadow)}.pf-c-dropdown__menu.pf-m-align-right{right:0}.pf-c-dropdown.pf-m-top .pf-c-dropdown__menu{--pf-c-dropdown__menu--Top:var(--pf-c-dropdown--m-top__menu--Top);transform:translateY(var(--pf-c-dropdown--m-top__menu--TranslateY))}.pf-c-dropdown__menu-item{display:block;width:100%;padding:var(--pf-c-dropdown__menu-item--PaddingTop) var(--pf-c-dropdown__menu-item--PaddingRight) var(--pf-c-dropdown__menu-item--PaddingBottom) var(--pf-c-dropdown__menu-item--PaddingLeft);font-size:var(--pf-c-dropdown__menu-item--FontSize);font-weight:var(--pf-c-dropdown__menu-item--FontWeight);line-height:var(--pf-c-dropdown__menu-item--LineHeight);color:var(--pf-c-dropdown__menu-item--Color);text-align:left;white-space:nowrap;background-color:var(--pf-c-dropdown__menu-item--BackgroundColor);border:none}.pf-c-dropdown__menu-item:focus,.pf-c-dropdown__menu-item:hover{--pf-c-dropdown__menu-item--Color:var(--pf-c-dropdown__menu-item--hover--Color);--pf-c-dropdown__menu-item--BackgroundColor:var(--pf-c-dropdown__menu-item--hover--BackgroundColor);text-decoration:none}.pf-c-dropdown__menu-item.pf-m-disabled,.pf-c-dropdown__menu-item:disabled{--pf-c-dropdown__menu-item--Color:var(--pf-c-dropdown__menu-item--disabled--Color);--pf-c-dropdown__menu-item--BackgroundColor:var(--pf-c-dropdown__menu-item--disabled--BackgroundColor);pointer-events:none}.pf-c-dropdown__menu-item.pf-m-icon{display:flex;align-items:center}.pf-c-dropdown__menu-item.pf-m-icon.pf-m-description{flex-direction:column;align-items:start}.pf-c-dropdown__menu-item.pf-m-icon .pf-c-dropdown__menu-item-main{display:flex;align-items:center}.pf-c-dropdown__menu-item.pf-m-text{--pf-c-dropdown__menu-item--Color:var(--pf-c-dropdown__menu-item--m-text--Color)}.pf-c-dropdown__menu-item.pf-m-text:focus,.pf-c-dropdown__menu-item.pf-m-text:hover{--pf-c-dropdown__menu-item--BackgroundColor:transparent}.pf-c-dropdown__menu-item-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--pf-c-dropdown__menu-item-icon--Width);height:var(--pf-c-dropdown__menu-item-icon--Height);margin-right:var(--pf-c-dropdown__menu-item-icon--MarginRight)}.pf-c-dropdown__menu-item-icon>*{max-width:100%;max-height:100%}.pf-c-dropdown__menu-item-description{font-size:var(--pf-c-dropdown__menu-item-description--FontSize);color:var(--pf-c-dropdown__menu-item-description--Color)}.pf-c-dropdown__group+.pf-c-dropdown__group{padding-top:var(--pf-c-dropdown__group--group--PaddingTop)}.pf-c-dropdown__group-title{padding:var(--pf-c-dropdown__group-title--PaddingTop) var(--pf-c-dropdown__group-title--PaddingRight) var(--pf-c-dropdown__group-title--PaddingBottom) var(--pf-c-dropdown__group-title--PaddingLeft);font-size:var(--pf-c-dropdown__group-title--FontSize);font-weight:var(--pf-c-dropdown__group-title--FontWeight);color:var(--pf-c-dropdown__group-title--Color)}.pf-c-empty-state{--pf-c-empty-state--PaddingTop:var(--pf-global--spacer--xl);--pf-c-empty-state--PaddingRight:var(--pf-global--spacer--xl);--pf-c-empty-state--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-empty-state--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-empty-state__content--MaxWidth:none;--pf-c-empty-state__icon--MarginBottom:var(--pf-global--spacer--lg);--pf-c-empty-state__icon--FontSize:var(--pf-global--icon--FontSize--xl);--pf-c-empty-state__icon--Color:var(--pf-global--icon--Color--light);--pf-c-empty-state__content--c-title--m-lg--FontSize:var(--pf-global--FontSize--xl);--pf-c-empty-state__body--MarginTop:var(--pf-global--spacer--md);--pf-c-empty-state__body--Color:var(--pf-global--Color--200);--pf-c-empty-state__primary--MarginTop:var(--pf-global--spacer--xl);--pf-c-empty-state__primary--secondary--MarginTop:var(--pf-global--spacer--sm);--pf-c-empty-state__secondary--MarginTop:var(--pf-global--spacer--xl);--pf-c-empty-state__secondary--MarginBottom:calc(var(--pf-global--spacer--xs)*-1);--pf-c-empty-state__secondary--child--MarginRight:calc(var(--pf-global--spacer--xs)/2);--pf-c-empty-state__secondary--child--MarginBottom:var(--pf-global--spacer--xs);--pf-c-empty-state__secondary--child--MarginLeft:calc(var(--pf-global--spacer--xs)/2);--pf-c-empty-state--m-xs__content--MaxWidth:21.875rem;--pf-c-empty-state--m-xs__body--FontSize:var(--pf-global--FontSize--sm);--pf-c-empty-state--m-xs--button--FontSize:var(--pf-global--FontSize--sm);--pf-c-empty-state--m-xs--PaddingTop:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs--PaddingRight:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs--PaddingBottom:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs--PaddingLeft:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs__icon--MarginBottom:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs__body--MarginTop:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs__primary--MarginTop:var(--pf-global--spacer--md);--pf-c-empty-state--m-xs__secondary--MarginTop:var(--pf-global--spacer--md);--pf-c-empty-state--m-sm__content--MaxWidth:25rem;--pf-c-empty-state--m-lg__content--MaxWidth:37.5rem;--pf-c-empty-state--m-xl__body--FontSize:var(--pf-global--FontSize--xl);--pf-c-empty-state--m-xl__body--MarginTop:var(--pf-global--spacer--lg);--pf-c-empty-state--m-xl__icon--MarginBottom:var(--pf-global--spacer--xl);--pf-c-empty-state--m-xl__icon--FontSize:6.25rem;--pf-c-empty-state--m-xl--c-button__secondary--MarginTop:var(--pf-global--spacer--md);display:flex;align-items:center;justify-content:center;padding:var(--pf-c-empty-state--PaddingTop) var(--pf-c-empty-state--PaddingRight) var(--pf-c-empty-state--PaddingBottom) var(--pf-c-empty-state--PaddingLeft);text-align:center}.pf-c-empty-state.pf-m-xs{--pf-c-empty-state--PaddingTop:var(--pf-c-empty-state--m-xs--PaddingTop);--pf-c-empty-state--PaddingRight:var(--pf-c-empty-state--m-xs--PaddingRight);--pf-c-empty-state--PaddingBottom:var(--pf-c-empty-state--m-xs--PaddingBottom);--pf-c-empty-state--PaddingLeft:var(--pf-c-empty-state--m-xs--PaddingLeft);--pf-c-empty-state__content--MaxWidth:var(--pf-c-empty-state--m-xs__content--MaxWidth);--pf-c-empty-state__icon--MarginBottom:var(--pf-c-empty-state--m-xs__icon--MarginBottom);--pf-c-empty-state__body--MarginTop:var(--pf-c-empty-state--m-xs__body--MarginTop);--pf-c-empty-state__primary--MarginTop:var(--pf-c-empty-state--m-xs__primary--MarginTop);--pf-c-empty-state__secondary--MarginTop:var(--pf-c-empty-state--m-xs__secondary--MarginTop)}.pf-c-empty-state.pf-m-xs .pf-c-empty-state__body{font-size:var(--pf-c-empty-state--m-xs__body--FontSize)}.pf-c-empty-state.pf-m-xs .pf-c-button{--pf-c-button--FontSize:var(--pf-c-empty-state--m-xs--button--FontSize)}.pf-c-empty-state.pf-m-sm{--pf-c-empty-state__content--MaxWidth:var(--pf-c-empty-state--m-sm__content--MaxWidth)}.pf-c-empty-state.pf-m-lg{--pf-c-empty-state__content--MaxWidth:var(--pf-c-empty-state--m-lg__content--MaxWidth)}.pf-c-empty-state.pf-m-xl{--pf-c-empty-state__body--MarginTop:var(--pf-c-empty-state--m-xl__body--MarginTop);--pf-c-empty-state__icon--MarginBottom:var(--pf-c-empty-state--m-xl__icon--MarginBottom);--pf-c-empty-state__icon--FontSize:var(--pf-c-empty-state--m-xl__icon--FontSize);--pf-c-empty-state--c-button__secondary--MarginTop:var(--pf-c-empty-state--m-xl--c-button__secondary--MarginTop)}.pf-c-empty-state.pf-m-xl .pf-c-empty-state__body{font-size:var(--pf-c-empty-state--m-xl__body--FontSize)}.pf-c-empty-state.pf-m-full-height{height:100%}.pf-c-empty-state__content{max-width:var(--pf-c-empty-state__content--MaxWidth)}.pf-c-empty-state__content>.pf-c-title.pf-m-lg{font-size:var(--pf-c-empty-state__content--c-title--m-lg--FontSize)}.pf-c-empty-state__icon{margin-bottom:var(--pf-c-empty-state__icon--MarginBottom);font-size:var(--pf-c-empty-state__icon--FontSize);color:var(--pf-c-empty-state__icon--Color)}.pf-c-empty-state__body{margin-top:var(--pf-c-empty-state__body--MarginTop);color:var(--pf-c-empty-state__body--Color)}.pf-c-empty-state__content>.pf-c-button.pf-m-primary,.pf-c-empty-state__primary{margin-top:var(--pf-c-empty-state__primary--MarginTop)}.pf-c-empty-state__content>.pf-c-button.pf-m-primary+.pf-c-empty-state__secondary,.pf-c-empty-state__primary+.pf-c-empty-state__secondary{margin-top:var(--pf-c-empty-state__primary--secondary--MarginTop)}.pf-c-empty-state__secondary{display:flex;flex-wrap:wrap;justify-content:center;margin-top:var(--pf-c-empty-state__secondary--MarginTop);margin-bottom:var(--pf-c-empty-state__secondary--MarginBottom)}.pf-c-empty-state__secondary>*{margin-right:var(--pf-c-empty-state__secondary--child--MarginRight);margin-bottom:var(--pf-c-empty-state__secondary--child--MarginBottom);margin-left:var(--pf-c-empty-state__secondary--child--MarginLeft)}.pf-m-overpass-font .pf-c-empty-state .pf-c-empty-state__content>.pf-c-title.pf-m-lg{font-size:var(--pf-global--FontSize--lg)}.pf-c-expandable-section{--pf-c-expandable-section__toggle--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-expandable-section__toggle--PaddingRight:var(--pf-global--spacer--md);--pf-c-expandable-section__toggle--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-expandable-section__toggle--PaddingLeft:0;--pf-c-expandable-section__toggle--Color:var(--pf-global--link--Color);--pf-c-expandable-section__toggle--hover--Color:var(--pf-global--link--Color--hover);--pf-c-expandable-section__toggle--active--Color:var(--pf-global--link--Color--hover);--pf-c-expandable-section__toggle--focus--Color:var(--pf-global--link--Color--hover);--pf-c-expandable-section__toggle--m-expanded--Color:var(--pf-global--link--Color--hover);--pf-c-expandable-section__toggle-icon--Color:var(--pf-global--Color--100);--pf-c-expandable-section__toggle-icon--Transition:.2s ease-in 0s;--pf-c-expandable-section__toggle-icon--Rotate:0;--pf-c-expandable-section--m-expanded__toggle-icon--Rotate:90deg;--pf-c-expandable-section__toggle-text--MarginLeft:calc(var(--pf-global--spacer--xs) + var(--pf-global--spacer--sm));--pf-c-expandable-section__content--MarginTop:var(--pf-global--spacer--md)}.pf-c-expandable-section.pf-m-expanded{--pf-c-expandable-section__toggle--Color:var(--pf-c-expandable-section__toggle--m-expanded--Color);--pf-c-expandable-section__toggle-icon--Rotate:var(--pf-c-expandable-section--m-expanded__toggle-icon--Rotate)}.pf-c-expandable-section__toggle{display:flex;padding:var(--pf-c-expandable-section__toggle--PaddingTop) var(--pf-c-expandable-section__toggle--PaddingRight) var(--pf-c-expandable-section__toggle--PaddingBottom) var(--pf-c-expandable-section__toggle--PaddingLeft);color:var(--pf-c-expandable-section__toggle--Color);border:none}.pf-c-expandable-section__toggle:hover{--pf-c-expandable-section__toggle--Color:var(--pf-c-expandable-section__toggle--hover--Color)}.pf-c-expandable-section__toggle.pf-m-active,.pf-c-expandable-section__toggle:active{--pf-c-expandable-section__toggle--Color:var(--pf-c-expandable-section__toggle--active--Color)}.pf-c-expandable-section__toggle:focus{--pf-c-expandable-section__toggle--Color:var(--pf-c-expandable-section__toggle--focus--Color)}.pf-c-expandable-section__toggle-icon{color:var(--pf-c-expandable-section__toggle-icon--Color);transition:var(--pf-c-expandable-section__toggle-icon--Transition);transform:rotate(var(--pf-c-expandable-section__toggle-icon--Rotate))}.pf-c-expandable-section__toggle-text{margin-left:var(--pf-c-expandable-section__toggle-text--MarginLeft)}.pf-c-expandable-section__content{margin-top:var(--pf-c-expandable-section__content--MarginTop)}.pf-m-overpass-font .pf-c-expandable-section__toggle{font-weight:var(--pf-global--FontWeight--semi-bold)}.pf-c-file-upload{--pf-c-file-upload--m-loading__file-details--before--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-file-upload--m-loading__file-details--before--Left:var(--pf-global--BorderWidth--sm);--pf-c-file-upload--m-loading__file-details--before--Right:var(--pf-global--BorderWidth--sm);--pf-c-file-upload--m-loading__file-details--before--Bottom:var(--pf-global--BorderWidth--sm);--pf-c-file-upload--m-drag-hover--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-file-upload--m-drag-hover--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-file-upload--m-drag-hover--before--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-file-upload--m-drag-hover--after--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-file-upload--m-drag-hover--after--Opacity:.1;--pf-c-file-upload__file-details__c-form-control--MinHeight:calc(var(--pf-global--spacer--3xl)*2);--pf-c-file-upload__file-select__c-button--m-control--OutlineOffset:calc(-1*var(--pf-global--spacer--xs));position:relative;display:flex;flex-direction:column}.pf-c-file-upload.pf-m-drag-hover:before{position:absolute;top:0;right:0;bottom:0;left:0;z-index:var(--pf-c-file-upload--m-drag-hover--before--ZIndex);content:"";border:var(--pf-c-file-upload--m-drag-hover--before--BorderWidth) solid var(--pf-c-file-upload--m-drag-hover--before--BorderColor)}.pf-c-file-upload.pf-m-drag-hover:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";background-color:var(--pf-c-file-upload--m-drag-hover--after--BackgroundColor);opacity:var(--pf-c-file-upload--m-drag-hover--after--Opacity)}.pf-c-file-upload.pf-m-loading .pf-c-file-upload__file-details{position:relative}.pf-c-file-upload.pf-m-loading .pf-c-file-upload__file-details:before{position:absolute;top:0;right:var(--pf-c-file-upload--m-loading__file-details--before--Left);bottom:var(--pf-c-file-upload--m-loading__file-details--before--Left);left:var(--pf-c-file-upload--m-loading__file-details--before--Left);content:"";background-color:var(--pf-c-file-upload--m-loading__file-details--before--BackgroundColor)}.pf-c-file-upload__file-select .pf-c-button.pf-m-control{outline-offset:var(--pf-c-file-upload__file-select__c-button--m-control--OutlineOffset)}.pf-c-file-upload__file-details{position:relative;display:flex}.pf-c-file-upload__file-details .pf-c-form-control{flex:1 1 auto;min-height:var(--pf-c-file-upload__file-details__c-form-control--MinHeight);border-top:0}.pf-c-file-upload__file-details-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.pf-c-form{--pf-c-form--GridGap:var(--pf-global--gutter--md);--pf-c-form__group--m-action--MarginTop:var(--pf-global--spacer--xl);--pf-c-form--m-horizontal__group-label--md--GridColumnWidth:9.375rem;--pf-c-form--m-horizontal__group-label--md--GridColumnGap:var(--pf-global--spacer--md);--pf-c-form--m-horizontal__group-control--md--GridColumnWidth:1fr;--pf-c-form--m-limit-width--MaxWidth:31.25rem;--pf-c-form--m-horizontal__group-label--md--PaddingTop:var(--pf-global--spacer--sm);--pf-c-form__group-label--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-form__label--FontSize:var(--pf-global--FontSize--sm);--pf-c-form__label--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-form__label--m-disabled--Color:var(--pf-global--disabled-color--100);--pf-c-form__label-text--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-form__label-required--MarginLeft:var(--pf-global--spacer--xs);--pf-c-form__label-required--FontSize:var(--pf-global--FontSize--sm);--pf-c-form__label-required--Color:var(--pf-global--danger-color--100);--pf-c-form__group-label-help--PaddingTop:var(--pf-global--spacer--xs);--pf-c-form__group-label-help--PaddingRight:var(--pf-global--spacer--xs);--pf-c-form__group-label-help--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-form__group-label-help--PaddingLeft:var(--pf-global--spacer--xs);--pf-c-form__group-label-help--MarginTop:calc(var(--pf-c-form__group-label-help--PaddingTop)*-1);--pf-c-form__group-label-help--MarginRight:calc(var(--pf-c-form__group-label-help--PaddingRight)*-1);--pf-c-form__group-label-help--MarginBottom:calc(var(--pf-c-form__group-label-help--PaddingBottom)*-1);--pf-c-form__group-label-help--MarginLeft:calc(var(--pf-c-form__group-label-help--PaddingLeft)*-1 + var(--pf-global--spacer--xs));--pf-c-form__group-label-help--FontSize:var(--pf-global--FontSize--sm);--pf-c-form__group-label-help--TranslateY:0.125rem;--pf-c-form__group-control--m-inline--child--MarginRight:var(--pf-global--spacer--lg);--pf-c-form__group-control__helper-text--MarginBottom:var(--pf-global--spacer--xs);--pf-c-form__actions--child--MarginTop:var(--pf-global--spacer--sm);--pf-c-form__actions--child--MarginRight:var(--pf-global--spacer--sm);--pf-c-form__actions--child--MarginBottom:var(--pf-global--spacer--sm);--pf-c-form__actions--child--MarginLeft:var(--pf-global--spacer--sm);--pf-c-form__actions--MarginTop:calc(var(--pf-c-form__actions--child--MarginTop)*-1);--pf-c-form__actions--MarginRight:calc(var(--pf-c-form__actions--child--MarginRight)*-1);--pf-c-form__actions--MarginBottom:calc(var(--pf-c-form__actions--child--MarginBottom)*-1);--pf-c-form__actions--MarginLeft:calc(var(--pf-c-form__actions--child--MarginLeft)*-1);--pf-c-form__helper-text--MarginTop:var(--pf-global--spacer--xs);--pf-c-form__helper-text--FontSize:var(--pf-global--FontSize--sm);--pf-c-form__helper-text--Color:var(--pf-global--Color--100);--pf-c-form__helper-text-icon--FontSize:var(--pf-global--FontSize--md);--pf-c-form__helper-text-icon--MarginRight:var(--pf-global--spacer--xs);--pf-c-form__helper-text--m-success--Color:var(--pf-global--success-color--200);--pf-c-form__helper-text--m-warning--Color:var(--pf-global--warning-color--200);--pf-c-form__helper-text--m-error--Color:var(--pf-global--danger-color--100);--pf-c-form__section--MarginTop:var(--pf-global--spacer--xl);--pf-c-form__section--Gap:var(--pf-global--gutter--md);--pf-c-form__field-group--border-width-base:var(--pf-global--BorderWidth--sm);--pf-c-form__field-group--BorderTopWidth:var(--pf-c-form__field-group--border-width-base);--pf-c-form__field-group--BorderTopColor:var(--pf-global--BorderColor--100);--pf-c-form__field-group--BorderBottomWidth:var(--pf-c-form__field-group--border-width-base);--pf-c-form__field-group--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-form__field-group--field-group--MarginTop:calc(var(--pf-c-form--GridGap)*-1);--pf-c-form__field-group--GridTemplateColumns--toggle:calc(var(--pf-global--spacer--md)*2 + var(--pf-c-form__field-group-toggle-icon--MinWidth) + var(--pf-global--spacer--xs));--pf-c-form__field-group-toggle--PaddingTop:var(--pf-global--spacer--md);--pf-c-form__field-group-toggle--PaddingRight:var(--pf-global--spacer--xs);--pf-c-form__field-group__field-group__field-group-toggle--PaddingTop:var(--pf-global--spacer--lg);--pf-c-form__field-group-header-toggle--BorderWidth--base:var(--pf-global--BorderWidth--sm);--pf-c-form__field-group__field-group--field-group__field-group-toggle--after--BorderTopWidth:var(--pf-c-form__field-group-header-toggle--BorderWidth--base);--pf-c-form__field-group-toggle-button--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-form__field-group-toggle-button--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-form__field-group-toggle-icon--Transition:var(--pf-global--Transition);--pf-c-form__field-group-toggle-icon--MinWidth:var(--pf-global--FontSize--md);--pf-c-form__field-group-toggle-icon--Rotate:0;--pf-c-form__field-group--m-expanded__toggle-icon--Rotate:90deg;--pf-c-form__field-group-header--PaddingTop:var(--pf-global--spacer--md);--pf-c-form__field-group-header--PaddingBottom:var(--pf-global--spacer--md);--pf-c-form__field-group-header--GridColumn:1/3;--pf-c-form__field-group__field-group__field-group-header--PaddingTop:var(--pf-global--spacer--lg);--pf-c-form__field-group__field-group__field-group-header--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-form__field-group-toggle--field-group-header--GridColumn:2/3;--pf-c-form__field-group__field-group--field-group__field-group-header--after--BorderTopWidth:var(--pf-c-form__field-group-header-toggle--BorderWidth--base);--pf-c-form__field-group-header-description--MarginTop:var(--pf-global--spacer--xs);--pf-c-form__field-group-header-description--Color:var(--pf-global--Color--200);--pf-c-form__field-group-header-actions--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-form__field-group-header-actions--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-form__field-group-header-actions--MarginLeft:var(--pf-global--spacer--sm);--pf-c-form__field-group-body--PaddingTop:var(--pf-global--spacer--lg);--pf-c-form__field-group-body--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-form__field-group-body--Gap:var(--pf-c-form--GridGap);--pf-c-form__field-group-body--GridColumn:2/3;--pf-c-form__field-group__field-group__field-group-body--GridColumn:1/3;--pf-c-form__field-group__field-group__field-group-toggle--field-group-body--GridColumn:2/3;--pf-c-form__field-group__field-group--not--m-expandable__field-group--not-m-expandable__field-group-header--GridColumn:2/3;--pf-c-form__field-group__field-group--not--m-expandable__field-group--not-m-expandable__field-group-body--GridColumn:2/3;--pf-c-form__field-group-body__field-group--last-child--MarginBottom:calc(var(--pf-c-form__field-group-body--PaddingBottom)*-1);display:grid;grid-gap:var(--pf-c-form--GridGap)}.pf-c-form.pf-m-horizontal{--pf-c-form__group-label--PaddingBottom:0}.pf-c-form.pf-m-horizontal.pf-m-align-right .pf-c-form__label{text-align:right}@media (min-width:768px){.pf-c-form.pf-m-horizontal .pf-c-form__group{display:grid;grid-column-gap:var(--pf-c-form--m-horizontal__group-label--md--GridColumnGap);grid-template-columns:var(--pf-c-form--m-horizontal__group-label--md--GridColumnWidth) var(--pf-c-form--m-horizontal__group-control--md--GridColumnWidth)}.pf-c-form.pf-m-horizontal .pf-c-form__group-label{padding-top:var(--pf-c-form--m-horizontal__group-label--md--PaddingTop)}.pf-c-form.pf-m-horizontal .pf-c-form__group-label.pf-m-no-padding-top{--pf-c-form--m-horizontal__group-label--md--PaddingTop:0}.pf-c-form.pf-m-horizontal .pf-c-form__group-control{grid-column:2}}.pf-c-form.pf-m-limit-width{max-width:var(--pf-c-form--m-limit-width--MaxWidth)}.pf-c-form__group.pf-m-action{margin-top:var(--pf-c-form__group--m-action--MarginTop);overflow:hidden}.pf-c-form__section{display:grid;gap:var(--pf-c-form__section--Gap)}.pf-c-form__section+.pf-c-form__group:not(.pf-m-action),.pf-c-form__section:not(:first-child){margin-top:var(--pf-c-form__section--MarginTop)}.pf-c-form__group-label{--pf-c-form__helper-text--MarginTop:0;padding-bottom:var(--pf-c-form__group-label--PaddingBottom)}.pf-c-form__label{font-size:var(--pf-c-form__label--FontSize);line-height:var(--pf-c-form__label--LineHeight)}.pf-c-form__label::selection{background-color:none}.pf-c-form__label:not(.pf-m-disabled):hover{cursor:pointer}.pf-c-form__label.pf-m-disabled{color:var(--pf-c-form__label--m-disabled--Color)}.pf-c-form__label.pf-m-disabled:hover{cursor:not-allowed}.pf-c-form__label-text{font-weight:var(--pf-c-form__label-text--FontWeight)}.pf-c-form__label-required{margin-left:var(--pf-c-form__label-required--MarginLeft);font-size:var(--pf-c-form__label-required--FontSize);color:var(--pf-c-form__label-required--Color)}.pf-c-form__group-label-help{padding:var(--pf-c-form__group-label-help--PaddingTop) var(--pf-c-form__group-label-help--PaddingRight) var(--pf-c-form__group-label-help--PaddingBottom) var(--pf-c-form__group-label-help--PaddingLeft);margin:var(--pf-c-form__group-label-help--MarginTop) var(--pf-c-form__group-label-help--MarginRight) var(--pf-c-form__group-label-help--MarginBottom) var(--pf-c-form__group-label-help--MarginLeft);font-size:var(--pf-c-form__group-label-help--FontSize);line-height:1;border:0;transform:translateY(var(--pf-c-form__group-label-help--TranslateY))}.pf-c-form__group-control.pf-m-inline{display:flex;flex-flow:row wrap}.pf-c-form__group-control.pf-m-inline>*{margin-right:var(--pf-c-form__group-control--m-inline--child--MarginRight)}.pf-c-form__group-control .pf-c-form__helper-text:first-child{--pf-c-form__helper-text--MarginTop:0;margin-bottom:var(--pf-c-form__group-control__helper-text--MarginBottom)}.pf-c-form__helper-text{margin-top:var(--pf-c-form__helper-text--MarginTop);font-size:var(--pf-c-form__helper-text--FontSize);color:var(--pf-c-form__helper-text--Color)}.pf-c-form__helper-text.pf-m-error{--pf-c-form__helper-text--Color:var(--pf-c-form__helper-text--m-error--Color)}.pf-c-form__helper-text.pf-m-success{--pf-c-form__helper-text--Color:var(--pf-c-form__helper-text--m-success--Color)}.pf-c-form__helper-text.pf-m-warning{--pf-c-form__helper-text--Color:var(--pf-c-form__helper-text--m-warning--Color)}.pf-c-form__helper-text.pf-m-inactive{display:none;visibility:hidden}.pf-c-form__helper-text.pf-m-hidden{visibility:hidden;opacity:0}.pf-c-form__helper-text-icon{margin-right:var(--pf-c-form__helper-text-icon--MarginRight);font-size:var(--pf-c-form__helper-text-icon--FontSize)}.pf-c-form__fieldset{border:0}.pf-c-form__actions{display:flex;flex-wrap:wrap;margin:var(--pf-c-form__actions--MarginTop) var(--pf-c-form__actions--MarginRight) var(--pf-c-form__actions--MarginBottom) var(--pf-c-form__actions--MarginLeft)}.pf-c-form__actions>*{margin:var(--pf-c-form__actions--child--MarginTop) var(--pf-c-form__actions--child--MarginRight) var(--pf-c-form__actions--child--MarginBottom) var(--pf-c-form__actions--child--MarginLeft)}.pf-c-form__field-group{--pf-c-form__field-group--BorderTopWidth:var(--pf-c-form__field-group--border-width-base);display:grid;grid-template-columns:minmax(var(--pf-c-form__field-group--GridTemplateColumns--toggle),max-content) 1fr;border-top:var(--pf-c-form__field-group--BorderTopWidth) solid var(--pf-c-form__field-group--BorderTopColor);border-bottom:var(--pf-c-form__field-group--BorderBottomWidth) solid var(--pf-c-form__field-group--BorderBottomColor)}.pf-c-form__field-group:last-child{--pf-c-form__field-group--BorderBottomWidth:0}.pf-c-form__field-group+.pf-c-form__field-group,.pf-c-form__field-group:first-child{--pf-c-form__field-group--BorderTopWidth:0}.pf-c-form__field-group+.pf-c-form__field-group{margin-top:var(--pf-c-form__field-group--field-group--MarginTop)}.pf-c-form__field-group .pf-c-form__field-group{--pf-c-form__field-group-body--GridColumn:var(--pf-c-form__field-group__field-group__field-group-body--GridColumn);--pf-c-form__field-group-toggle--PaddingTop:var(--pf-c-form__field-group__field-group__field-group-toggle--PaddingTop);--pf-c-form__field-group-header--PaddingTop:var(--pf-c-form__field-group__field-group__field-group-header--PaddingTop);--pf-c-form__field-group-header--PaddingBottom:var(--pf-c-form__field-group__field-group__field-group-header--PaddingBottom);--pf-c-form__field-group-body--PaddingTop:0}.pf-c-form__field-group .pf-c-form__field-group .pf-c-form__field-group-toggle~.pf-c-form__field-group-body{--pf-c-form__field-group-body--GridColumn:var(--pf-c-form__field-group__field-group__field-group-toggle--field-group-body--GridColumn)}.pf-c-form__field-group.pf-m-expanded>.pf-c-form__field-group-toggle{--pf-c-form__field-group-toggle-icon--Rotate:var(--pf-c-form__field-group--m-expanded__toggle-icon--Rotate)}.pf-c-form__field-group-toggle{grid-column:1/2;grid-row:1/2;padding-top:var(--pf-c-form__field-group-toggle--PaddingTop);padding-right:var(--pf-c-form__field-group-toggle--PaddingRight)}.pf-c-form__field-group-toggle+.pf-c-form__field-group-header{--pf-c-form__field-group-header--GridColumn:var(--pf-c-form__field-group-toggle--field-group-header--GridColumn)}.pf-c-form__field-group-toggle-button{margin-top:var(--pf-c-form__field-group-toggle-button--MarginTop);margin-bottom:var(--pf-c-form__field-group-toggle-button--MarginBottom)}.pf-c-form__field-group-toggle-icon{display:inline-block;min-width:var(--pf-c-form__field-group-toggle-icon--MinWidth);text-align:center;transition:var(--pf-c-form__field-group-toggle-icon--Transition);transform:rotate(var(--pf-c-form__field-group-toggle-icon--Rotate))}.pf-c-form__field-group-header{grid-column:var(--pf-c-form__field-group-header--GridColumn);grid-row:1/2;display:flex;align-items:flex-start;padding-top:var(--pf-c-form__field-group-header--PaddingTop);padding-bottom:var(--pf-c-form__field-group-header--PaddingBottom)}.pf-c-form__field-group-header-main{display:flex;flex-direction:column;flex-grow:1}.pf-c-form__field-group-header-title{display:flex}.pf-c-form__field-group-header-title-text{flex-grow:1}.pf-c-form__field-group-header-description{margin-top:var(--pf-c-form__field-group-header-description--MarginTop);color:var(--pf-c-form__field-group-header-description--Color)}.pf-c-form__field-group-header-actions{margin-top:var(--pf-c-form__field-group-header-actions--MarginTop);margin-bottom:var(--pf-c-form__field-group-header-actions--MarginBottom);margin-left:var(--pf-c-form__field-group-header-actions--MarginLeft);white-space:nowrap}.pf-c-form__field-group-body{grid-column:var(--pf-c-form__field-group-body--GridColumn);display:grid;gap:var(--pf-c-form__field-group-body--Gap);padding-top:var(--pf-c-form__field-group-body--PaddingTop);padding-bottom:var(--pf-c-form__field-group-body--PaddingBottom)}.pf-c-form__field-group-body>.pf-c-form__field-group:first-child{--pf-c-form__field-group-toggle--PaddingTop:0;--pf-c-form__field-group-header--PaddingTop:0}.pf-c-form__field-group-body>.pf-c-form__field-group:last-child{margin-bottom:var(--pf-c-form__field-group-body__field-group--last-child--MarginBottom)}.pf-c-form-control{--pf-c-form-control--FontSize:var(--pf-global--FontSize--md);--pf-c-form-control--LineHeight:var(--pf-global--LineHeight--md);--pf-c-form-control--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-form-control--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-form-control--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-form-control--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-form-control--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-form-control--BorderRadius:0;--pf-c-form-control--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-form-control--Height:calc(var(--pf-c-form-control--FontSize)*var(--pf-c-form-control--LineHeight) + var(--pf-c-form-control--BorderWidth)*2 + var(--pf-c-form-control--PaddingTop) + var(--pf-c-form-control--PaddingBottom));--pf-c-form-control--inset--base:var(--pf-global--spacer--sm);--pf-c-form-control--PaddingTop:calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));--pf-c-form-control--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));--pf-c-form-control--PaddingRight:var(--pf-c-form-control--inset--base);--pf-c-form-control--PaddingLeft:var(--pf-c-form-control--inset--base);--pf-c-form-control--hover--BorderBottomColor:var(--pf-global--primary-color--100);--pf-c-form-control--focus--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-form-control--focus--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--focus--BorderBottomWidth));--pf-c-form-control--focus--BorderBottomColor:var(--pf-global--primary-color--100);--pf-c-form-control--m-expanded--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-form-control--m-expanded--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--focus--BorderBottomWidth));--pf-c-form-control--m-expanded--BorderBottomColor:var(--pf-global--primary-color--100);--pf-c-form-control--placeholder--Color:var(--pf-global--Color--dark-200);--pf-c-form-control--disabled--Color:var(--pf-global--disabled-color--100);--pf-c-form-control--disabled--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-form-control--disabled--BorderColor:transparent;--pf-c-form-control--readonly--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-form-control--readonly--hover--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-form-control--readonly--focus--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));--pf-c-form-control--readonly--focus--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-form-control--readonly--focus--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-form-control--success--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-form-control--success--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--success--BorderBottomWidth));--pf-c-form-control--success--BorderBottomColor:var(--pf-global--success-color--100);--pf-c-form-control--success--PaddingRight:var(--pf-global--spacer--xl);--pf-c-form-control--success--BackgroundPositionX:calc(100% - var(--pf-c-form-control--PaddingLeft));--pf-c-form-control--success--BackgroundPositionY:center;--pf-c-form-control--success--BackgroundPosition:var(--pf-c-form-control--success--BackgroundPositionX) var(--pf-c-form-control--success--BackgroundPositionY);--pf-c-form-control--success--BackgroundSizeX:var(--pf-c-form-control--FontSize);--pf-c-form-control--success--BackgroundSizeY:var(--pf-c-form-control--FontSize);--pf-c-form-control--success--BackgroundSize:var(--pf-c-form-control--success--BackgroundSizeX) var(--pf-c-form-control--success--BackgroundSizeY);--pf-c-form-control--success--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%233e8635' d='M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z'/%3E%3C/svg%3E");--pf-c-form-control--m-warning--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-form-control--m-warning--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--m-warning--BorderBottomWidth));--pf-c-form-control--m-warning--BorderBottomColor:var(--pf-global--warning-color--100);--pf-c-form-control--m-warning--PaddingRight:var(--pf-global--spacer--xl);--pf-c-form-control--m-warning--BackgroundPositionX:calc(100% - var(--pf-c-form-control--PaddingLeft) - 0.0625rem);--pf-c-form-control--m-warning--BackgroundPositionY:center;--pf-c-form-control--m-warning--BackgroundPosition:var(--pf-c-form-control--m-warning--BackgroundPositionX) var(--pf-c-form-control--m-warning--BackgroundPositionY);--pf-c-form-control--m-warning--BackgroundSizeX:1.25rem;--pf-c-form-control--m-warning--BackgroundSizeY:var(--pf-c-form-control--FontSize);--pf-c-form-control--m-warning--BackgroundSize:var(--pf-c-form-control--m-warning--BackgroundSizeX) var(--pf-c-form-control--m-warning--BackgroundSizeY);--pf-c-form-control--m-warning--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23f0ab00' d='M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z'/%3E%3C/svg%3E");--pf-c-form-control--invalid--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-form-control--invalid--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--invalid--BorderBottomWidth));--pf-c-form-control--invalid--BorderBottomColor:var(--pf-global--danger-color--100);--pf-c-form-control--invalid--PaddingRight:var(--pf-global--spacer--xl);--pf-c-form-control--invalid--BackgroundPositionX:calc(100% - var(--pf-c-form-control--PaddingLeft));--pf-c-form-control--invalid--BackgroundPositionY:center;--pf-c-form-control--invalid--BackgroundPosition:var(--pf-c-form-control--invalid--BackgroundPositionX) var(--pf-c-form-control--invalid--BackgroundPositionY);--pf-c-form-control--invalid--BackgroundSizeX:var(--pf-c-form-control--FontSize);--pf-c-form-control--invalid--BackgroundSizeY:var(--pf-c-form-control--FontSize);--pf-c-form-control--invalid--BackgroundSize:var(--pf-c-form-control--invalid--BackgroundSizeX) var(--pf-c-form-control--invalid--BackgroundSizeY);--pf-c-form-control--invalid--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23c9190b' d='M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z'/%3E%3C/svg%3E");--pf-c-form-control--invalid--exclamation--Background:var(--pf-c-form-control--invalid--BackgroundUrl) var(--pf-c-form-control--invalid--BackgroundPosition)/var(--pf-c-form-control--invalid--BackgroundSize) no-repeat;--pf-c-form-control--invalid--Background:var(--pf-c-form-control--BackgroundColor) var(--pf-c-form-control--invalid--exclamation--Background);--pf-c-form-control--m-search--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-form-control--m-search--BackgroundPosition:var(--pf-c-form-control--PaddingRight);--pf-c-form-control--m-search--BackgroundSize:var(--pf-c-form-control--FontSize) var(--pf-c-form-control--FontSize);--pf-c-form-control--m-search--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%236a6e73' d='M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z'/%3E%3C/svg%3E");--pf-c-form-control--m-icon--PaddingRight:calc(var(--pf-c-form-control--inset--base) + var(--pf-c-form-control--m-icon--BackgroundSizeX) + var(--pf-c-form-control--m-icon--icon--spacer));--pf-c-form-control--m-icon--BackgroundUrl:none;--pf-c-form-control--m-icon--BackgroundPositionX:calc(100% - var(--pf-c-form-control--inset--base));--pf-c-form-control--m-icon--BackgroundPositionY:center;--pf-c-form-control--m-icon--BackgroundSizeX:var(--pf-c-form-control--FontSize);--pf-c-form-control--m-icon--BackgroundSizeY:var(--pf-c-form-control--FontSize);--pf-c-form-control--m-icon--icon--spacer:var(--pf-global--spacer--sm);--pf-c-form-control--m-icon--icon--PaddingRight:calc(var(--pf-c-form-control--inset--base) + var(--pf-c-form-control--invalid--BackgroundSizeX) + var(--pf-c-form-control--m-icon--icon--spacer) + var(--pf-c-form-control--m-icon--BackgroundSizeX) + var(--pf-c-form-control--m-icon--icon--spacer));--pf-c-form-control--m-icon--icon--BackgroundPositionX:calc(var(--pf-c-form-control--m-icon--BackgroundPositionX) - var(--pf-c-form-control--m-icon--icon--spacer) - var(--pf-c-form-control--invalid--BackgroundSizeX));--pf-c-form-control--m-icon--invalid--BackgroundUrl:var(--pf-c-form-control--invalid--BackgroundUrl),var(--pf-c-form-control--m-icon--BackgroundUrl);--pf-c-form-control--m-icon--invalid--BackgroundPosition:var(--pf-c-form-control--invalid--BackgroundPosition),var(--pf-c-form-control--m-icon--icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);--pf-c-form-control--m-icon--invalid--BackgroundSize:var(--pf-c-form-control--invalid--BackgroundSize),var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY);--pf-c-form-control--m-icon--success--BackgroundUrl:var(--pf-c-form-control--success--BackgroundUrl),var(--pf-c-form-control--m-icon--BackgroundUrl);--pf-c-form-control--m-icon--success--BackgroundPosition:var(--pf-c-form-control--success--BackgroundPosition),var(--pf-c-form-control--m-icon--icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);--pf-c-form-control--m-icon--success--BackgroundSize:var(--pf-c-form-control--success--BackgroundSize),var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY);--pf-c-form-control--m-icon--m-warning--BackgroundUrl:var(--pf-c-form-control--m-warning--BackgroundUrl),var(--pf-c-form-control--m-icon--BackgroundUrl);--pf-c-form-control--m-icon--m-warning--BackgroundPosition:var(--pf-c-form-control--m-warning--BackgroundPosition),var(--pf-c-form-control--m-icon--icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);--pf-c-form-control--m-icon--m-warning--BackgroundSize:var(--pf-c-form-control--m-warning--BackgroundSize),var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY);--pf-c-form-control--m-calendar--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%236a6e73' d='M0 464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V192H0v272zm320-196c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zM192 268c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zM64 268c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12v-40zM400 64h-48V16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v48H160V16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v48H48C21.5 64 0 85.5 0 112v48h448v-48c0-26.5-21.5-48-48-48z'/%3E%3C/svg%3E");--pf-c-form-control--m-clock--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%236a6e73' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm61.8-104.4l-84.9-61.7c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v141.7l66.8 48.6c5.4 3.9 6.5 11.4 2.6 16.8L334.6 349c-3.9 5.3-11.4 6.5-16.8 2.6z'/%3E%3C/svg%3E");--pf-c-form-control__select--PaddingRight:var(--pf-global--spacer--lg);--pf-c-form-control__select--BackgroundUrl:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='%23urrentColor' d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'/%3E%3C/svg%3E");--pf-c-form-control__select--BackgroundSize:.625em;--pf-c-form-control__select--BackgroundPositionX:calc(100% - var(--pf-global--spacer--md) + 1px);--pf-c-form-control__select--BackgroundPositionY:center;--pf-c-form-control__select--BackgroundPosition:var(--pf-c-form-control__select--BackgroundPositionX) var(--pf-c-form-control__select--BackgroundPositionY);--pf-c-form-control__select--success--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-form-control__select--success--BackgroundPosition:calc(var(--pf-c-form-control__select--BackgroundPositionX) - var(--pf-global--spacer--lg));--pf-c-form-control__select--m-warning--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-form-control__select--m-warning--BackgroundPosition:calc(var(--pf-c-form-control__select--BackgroundPositionX) - var(--pf-global--spacer--lg) + 0.0625rem);--pf-c-form-control__select--invalid--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-form-control__select--invalid--BackgroundPosition:calc(var(--pf-c-form-control__select--BackgroundPositionX) - var(--pf-global--spacer--lg));--pf-c-form-control--textarea--success--BackgroundPositionY:var(--pf-c-form-control--PaddingLeft);--pf-c-form-control--textarea--m-warning--BackgroundPositionY:var(--pf-c-form-control--PaddingLeft);--pf-c-form-control--textarea--invalid--BackgroundPositionY:var(--pf-c-form-control--PaddingLeft);color:var(--pf-global--Color--100);width:100%;padding:var(--pf-c-form-control--PaddingTop) var(--pf-c-form-control--PaddingRight) var(--pf-c-form-control--PaddingBottom) var(--pf-c-form-control--PaddingLeft);font-size:var(--pf-c-form-control--FontSize);line-height:var(--pf-c-form-control--LineHeight);background-color:var(--pf-c-form-control--BackgroundColor);background-repeat:no-repeat;border:var(--pf-c-form-control--BorderWidth) solid;border-color:var(--pf-c-form-control--BorderTopColor) var(--pf-c-form-control--BorderRightColor) var(--pf-c-form-control--BorderBottomColor) var(--pf-c-form-control--BorderLeftColor);border-radius:var(--pf-c-form-control--BorderRadius);-moz-appearance:none;-webkit-appearance:none}.pf-c-form-control::placeholder{color:var(--pf-c-form-control--placeholder--Color)}.pf-c-form-control:not(textarea){height:var(--pf-c-form-control--Height);text-overflow:ellipsis}.pf-c-form-control[readonly]{background-color:var(--pf-c-form-control--readonly--BackgroundColor)}.pf-c-form-control[readonly]:not(.pf-m-success):not([aria-invalid=true]):hover{--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--readonly--hover--BorderBottomColor)}.pf-c-form-control[readonly]:not(.pf-m-success):not([aria-invalid=true]):focus{--pf-c-form-control--focus--PaddingBottom:var(--pf-c-form-control--readonly--focus--PaddingBottom);--pf-c-form-control--focus--BorderBottomWidth:var(--pf-c-form-control--readonly--focus--BorderBottomWidth);--pf-c-form-control--focus--BorderBottomColor:var(--pf-c-form-control--readonly--focus--BorderBottomColor)}.pf-c-form-control:hover{--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--hover--BorderBottomColor)}.pf-c-form-control:focus{--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--focus--BorderBottomColor);padding-bottom:var(--pf-c-form-control--focus--PaddingBottom);border-bottom-width:var(--pf-c-form-control--focus--BorderBottomWidth)}.pf-c-form-control.pf-m-expanded{--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--m-expanded--BorderBottomColor);padding-bottom:var(--pf-c-form-control--m-expanded--PaddingBottom);border-bottom-width:var(--pf-c-form-control--m-expanded--BorderBottomWidth)}.pf-c-form-control:disabled{--pf-c-form-control--Color:var(--pf-c-form-control--disabled--Color);--pf-c-form-control--BackgroundColor:var(--pf-c-form-control--disabled--BackgroundColor);cursor:not-allowed;border-color:var(--pf-c-form-control--disabled--BorderColor)}.pf-c-form-control[aria-invalid=true]{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--invalid--PaddingRight);--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--invalid--BorderBottomColor);padding-bottom:var(--pf-c-form-control--invalid--PaddingBottom);background-image:var(--pf-c-form-control--invalid--BackgroundUrl);background-position:var(--pf-c-form-control--invalid--BackgroundPosition);background-size:var(--pf-c-form-control--invalid--BackgroundSize);border-bottom-width:var(--pf-c-form-control--invalid--BorderBottomWidth)}.pf-c-form-control[aria-invalid=true].pf-m-icon{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--m-icon--icon--PaddingRight);background-image:var(--pf-c-form-control--m-icon--invalid--BackgroundUrl);background-position:var(--pf-c-form-control--m-icon--invalid--BackgroundPosition);background-size:var(--pf-c-form-control--m-icon--invalid--BackgroundSize)}.pf-c-form-control.pf-m-success{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--success--PaddingRight);--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--success--BorderBottomColor);padding-bottom:var(--pf-c-form-control--success--PaddingBottom);background-image:var(--pf-c-form-control--success--BackgroundUrl);background-position:var(--pf-c-form-control--success--BackgroundPosition);background-size:var(--pf-c-form-control--success--BackgroundSize);border-bottom-width:var(--pf-c-form-control--success--BorderBottomWidth)}.pf-c-form-control.pf-m-success.pf-m-icon{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--m-icon--icon--PaddingRight);background-image:var(--pf-c-form-control--m-icon--success--BackgroundUrl);background-position:var(--pf-c-form-control--m-icon--success--BackgroundPosition);background-size:var(--pf-c-form-control--m-icon--success--BackgroundSize)}.pf-c-form-control.pf-m-warning{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--m-warning--PaddingRight);--pf-c-form-control--BorderBottomColor:var(--pf-c-form-control--m-warning--BorderBottomColor);padding-bottom:var(--pf-c-form-control--m-warning--PaddingBottom);background-image:var(--pf-c-form-control--m-warning--BackgroundUrl);background-position:var(--pf-c-form-control--m-warning--BackgroundPosition);background-size:var(--pf-c-form-control--m-warning--BackgroundSize);border-bottom-width:var(--pf-c-form-control--m-warning--BorderBottomWidth)}.pf-c-form-control.pf-m-warning.pf-m-icon{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--m-icon--icon--PaddingRight);background-image:var(--pf-c-form-control--m-icon--m-warning--BackgroundUrl);background-position:var(--pf-c-form-control--m-icon--m-warning--BackgroundPosition);background-size:var(--pf-c-form-control--m-icon--m-warning--BackgroundSize)}.pf-c-form-control.pf-m-search{--pf-c-form-control--PaddingLeft:var(--pf-c-form-control--m-search--PaddingLeft);background-image:var(--pf-c-form-control--m-search--BackgroundUrl);background-position:var(--pf-c-form-control--m-search--BackgroundPosition);background-size:var(--pf-c-form-control--m-search--BackgroundSize)}.pf-c-form-control.pf-m-icon{--pf-c-form-control--PaddingRight:var(--pf-c-form-control--m-icon--PaddingRight);background-image:var(--pf-c-form-control--m-icon--BackgroundUrl);background-position:var(--pf-c-form-control--m-icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);background-size:var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY)}.pf-c-form-control.pf-m-icon.pf-m-calendar{--pf-c-form-control--m-icon--BackgroundUrl:var(--pf-c-form-control--m-calendar--BackgroundUrl)}.pf-c-form-control.pf-m-icon.pf-m-clock{--pf-c-form-control--m-icon--BackgroundUrl:var(--pf-c-form-control--m-clock--BackgroundUrl)}select.pf-c-form-control{--pf-c-form-control--PaddingRight:var(--pf-c-form-control__select--PaddingRight);background-image:var(--pf-c-form-control__select--BackgroundUrl);background-position:var(--pf-c-form-control__select--BackgroundPosition);background-size:var(--pf-c-form-control__select--BackgroundSize)}select.pf-c-form-control[aria-invalid=true]{--pf-c-form-control--PaddingRight:var(--pf-c-form-control__select--invalid--PaddingRight);--pf-c-form-control--invalid--BackgroundPosition:var(--pf-c-form-control__select--invalid--BackgroundPosition);background-image:var(--pf-c-form-control__select--BackgroundUrl),var(--pf-c-form-control--invalid--BackgroundUrl);background-position:var(--pf-c-form-control__select--BackgroundPosition),var(--pf-c-form-control--invalid--BackgroundPosition);background-size:var(--pf-c-form-control__select--BackgroundSize),var(--pf-c-form-control--invalid--BackgroundSize)}select.pf-c-form-control.pf-m-success{--pf-c-form-control--PaddingRight:var(--pf-c-form-control__select--success--PaddingRight);--pf-c-form-control--success--BackgroundPosition:var(--pf-c-form-control__select--success--BackgroundPosition);background-image:var(--pf-c-form-control__select--BackgroundUrl),var(--pf-c-form-control--success--BackgroundUrl);background-position:var(--pf-c-form-control__select--BackgroundPosition),var(--pf-c-form-control--success--BackgroundPosition);background-size:var(--pf-c-form-control__select--BackgroundSize),var(--pf-c-form-control--success--BackgroundSize)}select.pf-c-form-control.pf-m-warning{--pf-c-form-control--PaddingRight:var(--pf-c-form-control__select--m-warning--PaddingRight);background-image:var(--pf-c-form-control__select--BackgroundUrl),var(--pf-c-form-control--m-warning--BackgroundUrl);background-position:var(--pf-c-form-control__select--BackgroundPosition),var(--pf-c-form-control__select--m-warning--BackgroundPosition);background-size:var(--pf-c-form-control__select--BackgroundSize),var(--pf-c-form-control--m-warning--BackgroundSize)}textarea.pf-c-form-control{--pf-c-form-control--success--BackgroundPositionY:var(--pf-c-form-control--textarea--success--BackgroundPositionY);--pf-c-form-control--invalid--BackgroundPositionY:var(--pf-c-form-control--textarea--invalid--BackgroundPositionY);--pf-c-form-control--m-warning--BackgroundPositionY:var(--pf-c-form-control--textarea--m-warning--BackgroundPositionY)}.pf-c-form-control.pf-m-resize-vertical{resize:vertical}.pf-c-form-control.pf-m-resize-horizontal{resize:horizontal}.pf-c-hint{--pf-c-hint--GridRowGap:var(--pf-global--spacer--md);--pf-c-hint--PaddingTop:var(--pf-global--spacer--lg);--pf-c-hint--PaddingRight:var(--pf-global--spacer--lg);--pf-c-hint--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-hint--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-hint--BackgroundColor:var(--pf-global--palette--blue-50);--pf-c-hint--BorderColor:var(--pf-global--palette--blue-100);--pf-c-hint--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-hint--BoxShadow:var(--pf-global--BoxShadow--sm);--pf-c-hint--Color:var(--pf-global--Color--100);--pf-c-hint__title--FontSize:var(--pf-global--FontSize--lg);--pf-c-hint__body--FontSize:var(--pf-global--FontSize--md);--pf-c-hint__footer--child--MarginRight:var(--pf-global--spacer--md);--pf-c-hint__actions--MarginLeft:var(--pf-global--spacer--2xl);--pf-c-hint__actions--c-dropdown--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);display:grid;grid-template-columns:1fr auto;grid-row-gap:var(--pf-c-hint--GridRowGap);padding:var(--pf-c-hint--PaddingTop) var(--pf-c-hint--PaddingRight) var(--pf-c-hint--PaddingBottom) var(--pf-c-hint--PaddingLeft);color:var(--pf-c-hint--Color);background-color:var(--pf-c-hint--BackgroundColor);border:var(--pf-c-hint--BorderWidth) solid var(--pf-c-hint--BorderColor);box-shadow:var(--pf-c-hint--BoxShadow)}.pf-c-hint .pf-c-button.pf-m-link.pf-m-inline{text-align:left;white-space:normal}.pf-c-hint__actions{display:inline-grid;grid-auto-flow:column;margin-left:var(--pf-c-hint__actions--MarginLeft);text-align:right;grid-column:2;grid-row:1}.pf-c-hint__actions .pf-c-dropdown .pf-c-dropdown__toggle.pf-m-plain{margin-top:var(--pf-c-hint__actions--c-dropdown--MarginTop)}.pf-c-hint__actions+.pf-c-hint__body{grid-column:1}.pf-c-hint__title{font-size:var(--pf-c-hint__title--FontSize)}.pf-c-hint__body{grid-column:1/-1;font-size:var(--pf-c-hint__body--FontSize)}.pf-c-hint__footer{grid-column:1/-1}.pf-c-hint__footer>:not(:last-child){margin-right:var(--pf-c-hint__footer--child--MarginRight)}.pf-c-inline-edit{--pf-c-inline-edit__group--item--MarginRight:var(--pf-global--spacer--sm);--pf-c-inline-edit__action--c-button--m-valid--m-plain--Color:var(--pf-global--link--Color);--pf-c-inline-edit__action--c-button--m-valid--m-plain--hover--Color:var(--pf-global--link--Color--hover);--pf-c-inline-edit__action--m-icon-group--item--MarginRight:0;--pf-c-inline-edit__group--m-footer--MarginTop:var(--pf-global--spacer--xl);--pf-c-inline-edit__label--m-bold--FontWeight:var(--pf-global--FontWeight--semi-bold)}.pf-c-inline-edit__group{display:flex;align-items:baseline}.pf-c-inline-edit__group>*{margin-right:var(--pf-c-inline-edit__group--item--MarginRight)}.pf-c-inline-edit__group.pf-m-icon-group{--pf-c-inline-edit__group--item--MarginRight:var(--pf-c-inline-edit__action--m-icon-group--item--MarginRight)}.pf-c-inline-edit__group.pf-m-footer{margin-top:var(--pf-c-inline-edit__group--m-footer--MarginTop)}.pf-c-inline-edit__group.pf-m-column{flex-direction:column}.pf-c-inline-edit__group.pf-m-column,.pf-c-inline-edit__group>:last-child{--pf-c-inline-edit__group--item--MarginRight:0}.pf-c-inline-edit__input{flex:1}.pf-c-inline-edit__action.pf-m-valid .pf-c-button.pf-m-plain{--pf-c-button--m-plain--Color:var(--pf-c-inline-edit__action--c-button--m-valid--m-plain--Color)}.pf-c-inline-edit__action.pf-m-valid .pf-c-button.pf-m-plain:hover{--pf-c-button--m-plain--Color:var(--pf-c-inline-edit__action--c-button--m-valid--m-plain--hover--Color)}.pf-c-inline-edit__action,.pf-c-inline-edit__group.pf-m-action-group,.pf-c-inline-edit__input{display:none;visibility:hidden}.pf-c-inline-edit__action.pf-m-enable-editable{display:inline-block;visibility:visible}.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__action,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__action,.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group,.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__input,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__input{visibility:visible}.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__input,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__input{display:block}.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__action,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__action{display:inline-block}.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group{display:inline-flex}.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__action.pf-m-enable-editable,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__action.pf-m-enable-editable,.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__value,.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__value{display:none;visibility:hidden}.pf-c-inline-edit__label+.pf-c-inline-edit__action.pf-m-enable>.pf-c-button{margin-top:calc(var(--pf-c-button--PaddingTop)*-1);margin-bottom:calc(var(--pf-c-button--PaddingBottom)*-1)}.pf-c-inline-edit__label.pf-m-bold{font-weight:var(--pf-c-inline-edit__label--m-bold--FontWeight)}.pf-c-input-group{--pf-c-input-group--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-input-group__text--FontSize:var(--pf-global--FontSize--md);--pf-c-input-group__text--PaddingRight:var(--pf-global--spacer--sm);--pf-c-input-group__text--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-input-group__text--Color:var(--pf-global--Color--dark-200);--pf-c-input-group__text--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-input-group__text--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-input-group__text--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-input-group__text--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-input-group__text--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-input-group__text--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-input-group__textarea--MinHeight:var(--pf-global--spacer--xl);--pf-c-input-group--c-form-control--invalid--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-input-group--c-form-control--MarginRight:0;color:var(--pf-global--Color--100);display:flex;width:100%;background-color:var(--pf-c-input-group--BackgroundColor)}.pf-c-input-group>*+*{margin-left:-1px}.pf-c-input-group .pf-c-form-control[aria-invalid=true]:not(:last-child){margin-right:var(--pf-c-input-group--c-form-control--MarginRight)}.pf-c-input-group input:not([type=checkbox]):not([type=radio]),.pf-c-input-group textarea{flex:2;min-width:0}.pf-c-input-group textarea{min-height:var(--pf-c-input-group__textarea--MinHeight)}.pf-c-input-group__text{display:flex;align-items:center;padding-right:var(--pf-c-input-group__text--PaddingRight);padding-left:var(--pf-c-input-group__text--PaddingLeft);font-size:var(--pf-c-input-group__text--FontSize);color:var(--pf-c-input-group__text--Color);text-align:center;background-color:var(--pf-c-input-group__text--BackgroundColor);border:var(--pf-c-input-group__text--BorderWidth) solid;border-color:var(--pf-c-input-group__text--BorderTopColor) var(--pf-c-input-group__text--BorderRightColor) var(--pf-c-input-group__text--BorderBottomColor) var(--pf-c-input-group__text--BorderLeftColor)}label.pf-c-input-group__text{cursor:pointer}.pf-c-input-group__text.pf-m-plain{--pf-c-input-group__text--BorderWidth:0;margin-left:0}.pf-c-jump-links{--pf-c-jump-links__list--PaddingTop:0;--pf-c-jump-links__list--PaddingRight:var(--pf-global--spacer--md);--pf-c-jump-links__list--PaddingBottom:0;--pf-c-jump-links__list--PaddingLeft:var(--pf-global--spacer--md);--pf-c-jump-links--m-vertical__list--PaddingTop:var(--pf-global--spacer--md);--pf-c-jump-links--m-vertical__list--PaddingRight:0;--pf-c-jump-links--m-vertical__list--PaddingBottom:var(--pf-global--spacer--md);--pf-c-jump-links--m-vertical__list--PaddingLeft:0;--pf-c-jump-links__list--FlexDirection:row;--pf-c-jump-links--m-vertical__list--FlexDirection:column;--pf-c-jump-links__list--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-jump-links__list--before--BorderTopWidth:var(--pf-global--BorderWidth--sm);--pf-c-jump-links__list--before--BorderRightWidth:0;--pf-c-jump-links__list--before--BorderBottomWidth:0;--pf-c-jump-links__list--before--BorderLeftWidth:0;--pf-c-jump-links--m-vertical__list--before--BorderLeftWidth:var(--pf-global--BorderWidth--sm);--pf-c-jump-links--m-vertical__list--before--BorderTopWidth:0;--pf-c-jump-links__list__list--MarginTop:calc(var(--pf-global--spacer--sm)*-1);--pf-c-jump-links__link--PaddingTop:var(--pf-global--spacer--md);--pf-c-jump-links__link--PaddingRight:var(--pf-global--spacer--md);--pf-c-jump-links__link--PaddingBottom:var(--pf-global--spacer--md);--pf-c-jump-links__link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-jump-links__list__list__link--PaddingTop:var(--pf-global--spacer--sm);--pf-c-jump-links__list__list__link--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-jump-links__list__list__link--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-jump-links__link--OutlineOffset:calc(-1*var(--pf-global--spacer--sm));--pf-c-jump-links__link--before--BorderTopWidth:0;--pf-c-jump-links__link--before--BorderRightWidth:0;--pf-c-jump-links__link--before--BorderBottomWidth:0;--pf-c-jump-links__link--before--BorderLeftWidth:0;--pf-c-jump-links__link--before--BorderColor:transparent;--pf-c-jump-links__link--focus--before--BorderTopWidth:var(--pf-global--BorderWidth--lg);--pf-c-jump-links__link--focus--before--BorderLeftWidth:0;--pf-c-jump-links__link--focus--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-jump-links__item--m-current__link--before--BorderTopWidth:var(--pf-global--BorderWidth--lg);--pf-c-jump-links__item--m-current__link--before--BorderLeftWidth:0;--pf-c-jump-links__item--m-current__link--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-jump-links--m-vertical__link--focus--before--BorderTopWidth:0;--pf-c-jump-links--m-vertical__link--focus--before--BorderLeftWidth:var(--pf-global--BorderWidth--lg);--pf-c-jump-links--m-vertical__item--m-current__link--before--BorderTopWidth:0;--pf-c-jump-links--m-vertical__item--m-current__link--before--BorderLeftWidth:var(--pf-global--BorderWidth--lg);--pf-c-jump-links__link-text--Color:var(--pf-global--Color--200);--pf-c-jump-links__link--hover__link-text--Color:var(--pf-global--Color--100);--pf-c-jump-links__link--focus__link-text--Color:var(--pf-global--Color--100);--pf-c-jump-links__item--m-current__link-text--Color:var(--pf-global--Color--100);--pf-c-jump-links__label--MarginBottom:var(--pf-global--spacer--md);display:flex}.pf-c-jump-links.pf-m-center{justify-content:center}.pf-c-jump-links.pf-m-center .pf-c-jump-links__main{align-items:center}.pf-c-jump-links.pf-m-vertical{--pf-c-jump-links__list--PaddingTop:var(--pf-c-jump-links--m-vertical__list--PaddingTop);--pf-c-jump-links__list--PaddingRight:var(--pf-c-jump-links--m-vertical__list--PaddingRight);--pf-c-jump-links__list--PaddingBottom:var(--pf-c-jump-links--m-vertical__list--PaddingBottom);--pf-c-jump-links__list--PaddingLeft:var(--pf-c-jump-links--m-vertical__list--PaddingLeft);--pf-c-jump-links__list--before--BorderTopWidth:var(--pf-c-jump-links--m-vertical__list--before--BorderTopWidth);--pf-c-jump-links__list--before--BorderLeftWidth:var(--pf-c-jump-links--m-vertical__list--before--BorderLeftWidth);--pf-c-jump-links__link--focus--before--BorderTopWidth:var(--pf-c-jump-links--m-vertical__link--focus--before--BorderTopWidth);--pf-c-jump-links__link--focus--before--BorderLeftWidth:var(--pf-c-jump-links--m-vertical__link--focus--before--BorderLeftWidth);--pf-c-jump-links__item--m-current__link--before--BorderTopWidth:var(--pf-c-jump-links--m-vertical__item--m-current__link--before--BorderTopWidth);--pf-c-jump-links__item--m-current__link--before--BorderLeftWidth:var(--pf-c-jump-links--m-vertical__item--m-current__link--before--BorderLeftWidth);--pf-c-jump-links__list--FlexDirection:var(--pf-c-jump-links--m-vertical__list--FlexDirection);flex-direction:column}.pf-c-jump-links__list{position:relative;display:flex;flex-direction:var(--pf-c-jump-links__list--FlexDirection);padding:var(--pf-c-jump-links__list--PaddingTop) var(--pf-c-jump-links__list--PaddingRight) var(--pf-c-jump-links__list--PaddingBottom) var(--pf-c-jump-links__list--PaddingLeft)}.pf-c-jump-links__list:before{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;content:"";border:solid var(--pf-c-jump-links__list--before--BorderColor);border-width:var(--pf-c-jump-links__list--before--BorderTopWidth) var(--pf-c-jump-links__list--before--BorderRightWidth) var(--pf-c-jump-links__list--before--BorderBottomWidth) var(--pf-c-jump-links__list--before--BorderLeftWidth)}.pf-c-jump-links__list .pf-c-jump-links__list{--pf-c-jump-links__list--PaddingTop:0;--pf-c-jump-links__list--PaddingBottom:0;--pf-c-jump-links__link--PaddingTop:var(--pf-c-jump-links__list__list__link--PaddingTop);--pf-c-jump-links__link--PaddingBottom:var(--pf-c-jump-links__list__list__link--PaddingBottom);--pf-c-jump-links__link--PaddingLeft:var(--pf-c-jump-links__list__list__link--PaddingLeft);margin-top:var(--pf-c-jump-links__list__list--MarginTop)}.pf-c-jump-links__link{position:relative;display:flex;flex:1;padding:var(--pf-c-jump-links__link--PaddingTop) var(--pf-c-jump-links__link--PaddingRight) var(--pf-c-jump-links__link--PaddingBottom) var(--pf-c-jump-links__link--PaddingLeft);text-decoration:none;outline-offset:var(--pf-c-jump-links__link--OutlineOffset)}.pf-c-jump-links__link:hover{--pf-c-jump-links__link-text--Color:var(--pf-c-jump-links__link--hover__link-text--Color)}.pf-c-jump-links__link:focus{--pf-c-jump-links__link-text--Color:var(--pf-c-jump-links__link--focus__link-text--Color);--pf-c-jump-links__link--before--BorderTopWidth:var(--pf-c-jump-links__link--focus--before--BorderTopWidth);--pf-c-jump-links__link--before--BorderLeftWidth:var(--pf-c-jump-links__link--focus--before--BorderLeftWidth);--pf-c-jump-links__link--before--BorderColor:var(--pf-c-jump-links__link--focus--before--BorderColor)}.pf-c-jump-links__link:before{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;content:"";border-left:var(--pf-c-jump-links__link--before--BorderLeftWidth) solid var(--pf-c-jump-links__link--before--BorderColor);border-bottom:var(--pf-c-jump-links__link--before--BorderBottomWidth) solid var(--pf-c-jump-links__link--before--BorderColor);border-right:var(--pf-c-jump-links__link--before--BorderRightWidth) solid var(--pf-c-jump-links__link--before--BorderColor);border-top:var(--pf-c-jump-links__link--before--BorderTopWidth) solid var(--pf-c-jump-links__link--before--BorderColor)}.pf-c-jump-links__item{--pf-c-jump-links__list--before--BorderColor:transparent}.pf-c-jump-links__item.pf-m-current>.pf-c-jump-links__link{--pf-c-jump-links__link--before--BorderTopWidth:var(--pf-c-jump-links__item--m-current__link--before--BorderTopWidth);--pf-c-jump-links__link--before--BorderLeftWidth:var(--pf-c-jump-links__item--m-current__link--before--BorderLeftWidth);--pf-c-jump-links__link--before--BorderColor:var(--pf-c-jump-links__item--m-current__link--before--BorderColor);--pf-c-jump-links__link-text--Color:var(--pf-c-jump-links__item--m-current__link-text--Color)}.pf-c-jump-links__link-text{color:var(--pf-c-jump-links__link-text--Color)}.pf-c-jump-links__label{margin-bottom:var(--pf-c-jump-links__label--MarginBottom)}.pf-c-jump-links__main{display:flex;flex-direction:column}.pf-c-label{--pf-c-label--PaddingTop:var(--pf-global--spacer--xs);--pf-c-label--PaddingRight:var(--pf-global--spacer--sm);--pf-c-label--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-label--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-label--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-label--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-label--Color:var(--pf-global--Color--100);--pf-c-label--FontSize:var(--pf-global--FontSize--sm);--pf-c-label__content--before--BorderWidth:0;--pf-c-label__content--before--BorderColor:transparent;--pf-c-label--m-outline--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-label--m-outline__content--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-label__content--link--hover--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-label__content--link--focus--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-global--BorderColor--200);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-global--BorderColor--200);--pf-c-label--m-outline__content--link--hover--before--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-label--m-outline__content--link--focus--before--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-label--m-blue--BackgroundColor:var(--pf-global--palette--blue-50);--pf-c-label--m-blue__content--Color:var(--pf-global--info-color--200);--pf-c-label--m-blue__icon--Color:var(--pf-global--primary-color--100);--pf-c-label--m-blue__content--link--hover--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-label--m-blue__content--link--focus--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-label--m-outline--m-blue__content--before--BorderColor:var(--pf-global--active-color--200);--pf-c-label--m-outline--m-blue__content--link--hover--before--BorderColor:var(--pf-global--active-color--200);--pf-c-label--m-outline--m-blue__content--link--focus--before--BorderColor:var(--pf-global--active-color--200);--pf-c-label--m-green--BackgroundColor:var(--pf-global--palette--green-50);--pf-c-label--m-green__content--Color:var(--pf-global--success-color--200);--pf-c-label--m-green__icon--Color:var(--pf-global--success-color--100);--pf-c-label--m-green__content--link--hover--before--BorderColor:var(--pf-global--success-color--100);--pf-c-label--m-green__content--link--focus--before--BorderColor:var(--pf-global--success-color--100);--pf-c-label--m-outline--m-green__content--before--BorderColor:var(--pf-global--palette--green-100);--pf-c-label--m-outline--m-green__content--link--hover--before--BorderColor:var(--pf-global--palette--green-100);--pf-c-label--m-outline--m-green__content--link--focus--before--BorderColor:var(--pf-global--palette--green-100);--pf-c-label--m-orange--BackgroundColor:var(--pf-global--palette--gold-50);--pf-c-label--m-orange__content--Color:var(--pf-global--palette--gold-700);--pf-c-label--m-orange__icon--Color:var(--pf-global--palette--orange-300);--pf-c-label--m-orange__content--link--hover--before--BorderColor:var(--pf-global--palette--orange-300);--pf-c-label--m-orange__content--link--focus--before--BorderColor:var(--pf-global--palette--orange-300);--pf-c-label--m-outline--m-orange__content--before--BorderColor:var(--pf-global--palette--gold-100);--pf-c-label--m-outline--m-orange__content--link--hover--before--BorderColor:var(--pf-global--palette--gold-100);--pf-c-label--m-outline--m-orange__content--link--focus--before--BorderColor:var(--pf-global--palette--gold-100);--pf-c-label--m-red--BackgroundColor:var(--pf-global--palette--red-50);--pf-c-label--m-red__content--Color:var(--pf-global--palette--red-300);--pf-c-label--m-red__icon--Color:var(--pf-global--danger-color--100);--pf-c-label--m-red__content--link--hover--before--BorderColor:var(--pf-global--danger-color--100);--pf-c-label--m-red__content--link--focus--before--BorderColor:var(--pf-global--danger-color--100);--pf-c-label--m-outline--m-red__content--before--BorderColor:var(--pf-global--danger-color--100);--pf-c-label--m-outline--m-red__content--link--hover--before--BorderColor:var(--pf-global--danger-color--100);--pf-c-label--m-outline--m-red__content--link--focus--before--BorderColor:var(--pf-global--danger-color--100);--pf-c-label--m-purple--BackgroundColor:var(--pf-global--palette--purple-50);--pf-c-label--m-purple__content--Color:var(--pf-global--palette--purple-700);--pf-c-label--m-purple__icon--Color:var(--pf-global--palette--purple-500);--pf-c-label--m-purple__content--link--hover--before--BorderColor:var(--pf-global--palette--purple-500);--pf-c-label--m-purple__content--link--focus--before--BorderColor:var(--pf-global--palette--purple-500);--pf-c-label--m-outline--m-purple__content--before--BorderColor:var(--pf-global--palette--purple-100);--pf-c-label--m-outline--m-purple__content--link--hover--before--BorderColor:var(--pf-global--palette--purple-100);--pf-c-label--m-outline--m-purple__content--link--focus--before--BorderColor:var(--pf-global--palette--purple-100);--pf-c-label--m-cyan--BackgroundColor:var(--pf-global--palette--cyan-50);--pf-c-label--m-cyan__content--Color:var(--pf-global--default-color--300);--pf-c-label--m-cyan__icon--Color:var(--pf-global--default-color--200);--pf-c-label--m-cyan__content--link--hover--before--BorderColor:var(--pf-global--default-color--200);--pf-c-label--m-cyan__content--link--focus--before--BorderColor:var(--pf-global--default-color--200);--pf-c-label--m-outline--m-cyan__content--before--BorderColor:var(--pf-global--palette--cyan-100);--pf-c-label--m-outline--m-cyan__content--link--hover--before--BorderColor:var(--pf-global--palette--cyan-100);--pf-c-label--m-outline--m-cyan__content--link--focus--before--BorderColor:var(--pf-global--palette--cyan-100);--pf-c-label--m-overflow__content--Color:var(--pf-global--link--Color);--pf-c-label--m-overflow__content--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-label--m-overflow__content--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-label--m-overflow__content--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-label--m-overflow__content--link--hover--before--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-label--m-overflow__content--link--hover--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-label--m-overflow__content--link--focus--before--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-label--m-overflow__content--link--focus--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-label__content--Color:var(--pf-global--Color--100);--pf-c-label__text--MaxWidth:16ch;--pf-c-label__icon--Color:var(--pf-global--Color--100);--pf-c-label__icon--MarginRight:var(--pf-global--spacer--xs);--pf-c-label__c-button--FontSize:var(--pf-global--FontSize--xs);--pf-c-label__c-button--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-label__c-button--MarginRight:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-label__c-button--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-label__c-button--MarginLeft:var(--pf-global--spacer--xs);--pf-c-label__c-button--PaddingTop:var(--pf-global--spacer--xs);--pf-c-label__c-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-label__c-button--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-label__c-button--PaddingLeft:var(--pf-global--spacer--sm);position:relative;padding:var(--pf-c-label--PaddingTop) var(--pf-c-label--PaddingRight) var(--pf-c-label--PaddingBottom) var(--pf-c-label--PaddingLeft);font-size:var(--pf-c-label--FontSize);color:var(--pf-c-label--Color);white-space:nowrap;background-color:var(--pf-c-label--BackgroundColor);border:0;border-radius:var(--pf-c-label--BorderRadius)}.pf-c-label.pf-m-blue{--pf-c-label--BackgroundColor:var(--pf-c-label--m-blue--BackgroundColor);--pf-c-label__content--Color:var(--pf-c-label--m-blue__content--Color);--pf-c-label__icon--Color:var(--pf-c-label--m-blue__icon--Color);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-c-label--m-outline--m-blue__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-blue__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-blue__content--link--focus--before--BorderColor);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-c-label--m-outline--m-blue__content--link--hover--before--BorderColor);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-c-label--m-outline--m-blue__content--link--focus--before--BorderColor)}.pf-c-label.pf-m-green{--pf-c-label--BackgroundColor:var(--pf-c-label--m-green--BackgroundColor);--pf-c-label__content--Color:var(--pf-c-label--m-green__content--Color);--pf-c-label__icon--Color:var(--pf-c-label--m-green__icon--Color);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-c-label--m-outline--m-green__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-green__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-green__content--link--focus--before--BorderColor);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-c-label--m-outline--m-green__content--link--hover--before--BorderColor);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-c-label--m-outline--m-green__content--link--focus--before--BorderColor)}.pf-c-label.pf-m-orange{--pf-c-label--BackgroundColor:var(--pf-c-label--m-orange--BackgroundColor);--pf-c-label__content--Color:var(--pf-c-label--m-orange__content--Color);--pf-c-label__icon--Color:var(--pf-c-label--m-orange__icon--Color);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-c-label--m-outline--m-orange__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-orange__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-orange__content--link--focus--before--BorderColor);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-c-label--m-outline--m-orange__content--link--hover--before--BorderColor);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-c-label--m-outline--m-orange__content--link--focus--before--BorderColor)}.pf-c-label.pf-m-red{--pf-c-label--BackgroundColor:var(--pf-c-label--m-red--BackgroundColor);--pf-c-label__content--Color:var(--pf-c-label--m-red__content--Color);--pf-c-label__icon--Color:var(--pf-c-label--m-red__icon--Color);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-c-label--m-outline--m-red__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-red__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-red__content--link--focus--before--BorderColor);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-c-label--m-outline--m-red__content--link--hover--before--BorderColor);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-c-label--m-outline--m-red__content--link--focus--before--BorderColor)}.pf-c-label.pf-m-purple{--pf-c-label--BackgroundColor:var(--pf-c-label--m-purple--BackgroundColor);--pf-c-label__content--Color:var(--pf-c-label--m-purple__content--Color);--pf-c-label__icon--Color:var(--pf-c-label--m-purple__icon--Color);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-c-label--m-outline--m-purple__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-purple__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-purple__content--link--focus--before--BorderColor);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-c-label--m-outline--m-purple__content--link--hover--before--BorderColor);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-c-label--m-outline--m-purple__content--link--focus--before--BorderColor)}.pf-c-label.pf-m-cyan{--pf-c-label--BackgroundColor:var(--pf-c-label--m-cyan--BackgroundColor);--pf-c-label__content--Color:var(--pf-c-label--m-cyan__content--Color);--pf-c-label__icon--Color:var(--pf-c-label--m-cyan__icon--Color);--pf-c-label--m-outline__content--before--BorderColor:var(--pf-c-label--m-outline--m-cyan__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-cyan__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-cyan__content--link--focus--before--BorderColor);--pf-c-label--m-outline__content--link--hover--before--BorderColor:var(--pf-c-label--m-outline--m-cyan__content--link--hover--before--BorderColor);--pf-c-label--m-outline__content--link--focus--before--BorderColor:var(--pf-c-label--m-outline--m-cyan__content--link--focus--before--BorderColor)}.pf-c-label.pf-m-outline{--pf-c-label__content--before--BorderWidth:var(--pf-c-label--m-outline__content--before--BorderWidth);--pf-c-label__content--before--BorderColor:var(--pf-c-label--m-outline__content--before--BorderColor);--pf-c-label--BackgroundColor:var(--pf-c-label--m-outline--BackgroundColor)}.pf-c-label.pf-m-outline a.pf-c-label__content:hover,.pf-c-label.pf-m-outline button.pf-c-label__content:hover,.pf-c-label.pf-m-overflow:hover{--pf-c-label__content--before--BorderWidth:var(--pf-c-label--m-outline__content--link--hover--before--BorderWidth);--pf-c-label__content--before--BorderColor:var(--pf-c-label--m-outline__content--link--hover--before--BorderColor)}.pf-c-label.pf-m-outline a.pf-c-label__content:focus,.pf-c-label.pf-m-outline button.pf-c-label__content:focus,.pf-c-label.pf-m-overflow:focus{--pf-c-label__content--before--BorderWidth:var(--pf-c-label--m-outline__content--link--focus--before--BorderWidth);--pf-c-label__content--before--BorderColor:var(--pf-c-label--m-outline__content--link--focus--before--BorderColor)}.pf-c-label .pf-c-button{--pf-c-button--FontSize:var(--pf-c-label__c-button--FontSize);--pf-c-button--PaddingTop:var(--pf-c-label__c-button--PaddingTop);--pf-c-button--PaddingRight:var(--pf-c-label__c-button--PaddingRight);--pf-c-button--PaddingBottom:var(--pf-c-label__c-button--PaddingBottom);--pf-c-button--PaddingLeft:var(--pf-c-label__c-button--PaddingLeft);margin:var(--pf-c-label__c-button--MarginTop) var(--pf-c-label__c-button--MarginRight) var(--pf-c-label__c-button--MarginBottom) var(--pf-c-label__c-button--MarginLeft)}.pf-c-label.pf-m-overflow{--pf-c-label__content--Color:var(--pf-c-label--m-overflow__content--Color);--pf-c-label--BackgroundColor:var(--pf-c-label--m-overflow__content--BackgroundColor);--pf-c-label__content--before--BorderWidth:var(--pf-c-label--m-overflow__content--before--BorderWidth);--pf-c-label__content--before--BorderColor:var(--pf-c-label--m-overflow__content--before--BorderColor);--pf-c-label__content--link--hover--before--BorderWidth:var(--pf-c-label--m-overflow__content--link--hover--before--BorderWidth);--pf-c-label__content--link--hover--before--BorderColor:var(--pf-c-label--m-overflow__content--link--hover--before--BorderColor);--pf-c-label__content--link--focus--before--BorderWidth:var(--pf-c-label--m-overflow__content--link--focus--before--BorderWidth);--pf-c-label__content--link--focus--before--BorderColor:var(--pf-c-label--m-overflow__content--link--focus--before--BorderColor)}.pf-c-label,.pf-c-label__content{display:inline-flex;align-items:center}.pf-c-label__text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:var(--pf-c-label__text--MaxWidth)}.pf-c-label__content{color:var(--pf-c-label__content--Color);border:0}.pf-c-label__content:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-label__content--before--BorderWidth) solid var(--pf-c-label__content--before--BorderColor);border-radius:var(--pf-c-label--BorderRadius)}a.pf-c-label__content,button.pf-c-label__content{cursor:pointer;border:none}a.pf-c-label__content,a.pf-c-label__content:focus,a.pf-c-label__content:hover,button.pf-c-label__content,button.pf-c-label__content:focus,button.pf-c-label__content:hover{text-decoration:none}a.pf-c-label__content:hover,button.pf-c-label__content:hover{--pf-c-label__content--before--BorderWidth:var(--pf-c-label__content--link--hover--before--BorderWidth);--pf-c-label__content--before--BorderColor:var(--pf-c-label__content--link--hover--before--BorderColor)}a.pf-c-label__content:focus,button.pf-c-label__content:focus{--pf-c-label__content--before--BorderWidth:var(--pf-c-label__content--link--focus--before--BorderWidth);--pf-c-label__content--before--BorderColor:var(--pf-c-label__content--link--focus--before--BorderColor)}.pf-c-label__icon{margin-right:var(--pf-c-label__icon--MarginRight);color:var(--pf-c-label__icon--Color)}.pf-c-label-group{--pf-c-label-group__list--MarginBottom:calc(var(--pf-global--spacer--xs)*-1);--pf-c-label-group__list--MarginRight:calc(var(--pf-global--spacer--xs)*-1);--pf-c-label-group--m-category--PaddingTop:var(--pf-global--spacer--xs);--pf-c-label-group--m-category--PaddingRight:var(--pf-global--spacer--xs);--pf-c-label-group--m-category--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-label-group--m-category--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-label-group--m-vertical--m-category--PaddingRight:var(--pf-global--spacer--sm);--pf-c-label-group--m-category--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-label-group--m-category--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-label-group--m-category--BorderColor:var(--pf-global--BorderColor--300);--pf-c-label-group--m-category--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-label-group__label--MarginRight:var(--pf-global--spacer--sm);--pf-c-label-group__label--MarginBottom:0;--pf-c-label-group--m-vertical__label--MarginBottom:var(--pf-global--spacer--sm);--pf-c-label-group__label--FontSize:var(--pf-global--FontSize--sm);--pf-c-label-group__label--MaxWidth:18ch;--pf-c-label-group__close--MarginTop:calc(var(--pf-global--spacer--xs)*-1);--pf-c-label-group__close--MarginBottom:calc(var(--pf-global--spacer--xs)*-1);--pf-c-label-group--m-vertical__close--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-label-group--m-vertical__close--MarginRight:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-label-group--m-vertical__close--MarginLeft:var(--pf-global--spacer--sm);--pf-c-label-group--m-vertical__close--c-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-label-group--m-vertical__close--c-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-label-group__list-item--MarginRight:var(--pf-global--spacer--xs);--pf-c-label-group__list-item--MarginBottom:var(--pf-global--spacer--xs);display:inline-flex}.pf-c-label-group.pf-m-category{padding:var(--pf-c-label-group--m-category--PaddingTop) var(--pf-c-label-group--m-category--PaddingRight) var(--pf-c-label-group--m-category--PaddingBottom) var(--pf-c-label-group--m-category--PaddingLeft);background-color:var(--pf-c-label-group--m-category--BackgroundColor);border:var(--pf-c-label-group--m-category--BorderWidth) solid var(--pf-c-label-group--m-category--BorderColor);border-radius:var(--pf-c-label-group--m-category--BorderRadius)}.pf-c-label-group.pf-m-vertical{--pf-c-label-group__list--MarginRight:0;--pf-c-label-group__list--MarginBottom:0;--pf-c-label-group__list-item--MarginRight:0;--pf-c-label-group__label--MarginRight:0;--pf-c-label-group__label--MarginBottom:var(--pf-c-label-group--m-vertical__label--MarginBottom);--pf-c-label-group__close--MarginTop:var(--pf-c-label-group--m-vertical__close--MarginTop);--pf-c-label-group__close--MarginLeft:var(--pf-c-label-group--m-vertical__close--MarginLeft);--pf-c-label-group__close--MarginBottom:0;--pf-c-label-group__close--MarginRight:var(--pf-c-label-group--m-vertical__close--MarginRight);--pf-c-label-group--m-category--PaddingRight:var(--pf-c-label-group--m-vertical--m-category--PaddingRight)}.pf-c-label-group.pf-m-vertical.pf-c-label-group{align-items:flex-start}.pf-c-label-group.pf-m-vertical .pf-c-label-group__list{flex-direction:column;align-items:flex-start}.pf-c-label-group.pf-m-vertical .pf-c-label-group__main{flex-direction:column}.pf-c-label-group.pf-m-vertical .pf-c-label-group__list-item:last-child{--pf-c-label-group__list-item--MarginBottom:0}.pf-c-label-group.pf-m-vertical .pf-c-label-group__close .pf-c-button{--pf-c-button--PaddingLeft:var(--pf-c-label-group--m-vertical__close--c-button--PaddingLeft);--pf-c-button--PaddingRight:var(--pf-c-label-group--m-vertical__close--c-button--PaddingRight)}.pf-c-label-group__main{display:flex;flex:1;flex-wrap:wrap;align-items:baseline}.pf-c-label-group__list{display:inline-flex;flex-wrap:wrap;margin-right:var(--pf-c-label-group__list--MarginRight);margin-bottom:var(--pf-c-label-group__list--MarginBottom)}.pf-c-label-group__list-item{display:inline-flex;margin-right:var(--pf-c-label-group__list-item--MarginRight);margin-bottom:var(--pf-c-label-group__list-item--MarginBottom)}.pf-c-label-group__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:var(--pf-c-label-group__label--MaxWidth);margin-right:var(--pf-c-label-group__label--MarginRight);margin-bottom:var(--pf-c-label-group__label--MarginBottom);font-size:var(--pf-c-label-group__label--FontSize)}.pf-c-label-group__close{margin:var(--pf-c-label-group__close--MarginTop) var(--pf-c-label-group__close--MarginRight) var(--pf-c-label-group__close--MarginBottom) var(--pf-c-label-group__close--MarginLeft)}.pf-c-list{--pf-c-list--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-list--nested--MarginTop:var(--pf-global--spacer--sm);--pf-c-list--nested--MarginLeft:var(--pf-global--spacer--sm);--pf-c-list--ul--ListStyle:var(--pf-global--ListStyle);--pf-c-list--li--MarginTop:var(--pf-global--spacer--sm);--pf-c-list--m-inline--li--MarginRight:var(--pf-global--spacer--lg);padding-left:var(--pf-c-list--PaddingLeft)}.pf-c-list ol,.pf-c-list ul{margin-top:var(--pf-c-list--nested--MarginTop);margin-left:var(--pf-c-list--nested--MarginLeft)}.pf-c-list li+li{margin-top:var(--pf-c-list--li--MarginTop)}ul.pf-c-list:not(.pf-m-inline){list-style:var(--pf-c-list--ul--ListStyle)}.pf-c-list.pf-m-inline{--pf-c-list--PaddingLeft:0;display:flex;flex-wrap:wrap}.pf-c-list.pf-m-inline li{--pf-c-list--li--MarginTop:0}.pf-c-list.pf-m-inline li:not(:last-child){margin-right:var(--pf-c-list--m-inline--li--MarginRight)}.pf-c-login{--pf-c-login--PaddingTop:var(--pf-global--spacer--lg);--pf-c-login--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-login--xl--BackgroundImage:none;--pf-c-login__container--xl--GridColumnGap:var(--pf-global--spacer--3xl);--pf-c-login__container--MaxWidth:31.25rem;--pf-c-login__container--xl--MaxWidth:none;--pf-c-login__container--PaddingLeft:6.125rem;--pf-c-login__container--PaddingRight:6.125rem;--pf-c-login__container--xl--GridTemplateColumns:34rem minmax(auto,34rem);--pf-c-login__header--MarginBottom:var(--pf-global--spacer--md);--pf-c-login__header--PaddingLeft:var(--pf-global--spacer--md);--pf-c-login__header--PaddingRight:var(--pf-global--spacer--md);--pf-c-login__header--xl--MarginBottom:var(--pf-global--spacer--2xl);--pf-c-login__header--xl--MarginTop:var(--pf-global--spacer--3xl);--pf-c-login__header--c-brand--MarginBottom:var(--pf-global--spacer--lg);--pf-c-login__header--c-brand--xl--MarginBottom:var(--pf-global--spacer--2xl);--pf-c-login__main--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-login__main--MarginBottom:var(--pf-global--spacer--lg);--pf-c-login__main-header--PaddingTop:var(--pf-global--spacer--2xl);--pf-c-login__main-header--PaddingRight:var(--pf-global--spacer--xl);--pf-c-login__main-header--PaddingBottom:var(--pf-global--spacer--md);--pf-c-login__main-header--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-login__main-header--md--PaddingRight:var(--pf-global--spacer--2xl);--pf-c-login__main-header--md--PaddingLeft:var(--pf-global--spacer--2xl);--pf-c-login__main-header--ColumnGap:var(--pf-global--spacer--md);--pf-c-login__main-header--RowGap:var(--pf-global--spacer--md);--pf-c-login__main-header-desc--MarginBottom:var(--pf-global--spacer--sm);--pf-c-login__main-header-desc--md--MarginBottom:0;--pf-c-login__main-header-desc--FontSize:var(--pf-global--FontSize--sm);--pf-c-login__main-body--PaddingRight:var(--pf-global--spacer--xl);--pf-c-login__main-body--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-login__main-body--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-login__main-body--md--PaddingRight:var(--pf-global--spacer--2xl);--pf-c-login__main-body--md--PaddingLeft:var(--pf-global--spacer--2xl);--pf-c-login__main-footer--PaddingBottom:var(--pf-global--spacer--3xl);--pf-c-login__main-footer--c-title--MarginBottom:var(--pf-global--spacer--md);--pf-c-login__main-footer-links--PaddingTop:var(--pf-global--spacer--sm);--pf-c-login__main-footer-links--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-login__main-footer-links--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-login__main-footer-links--PaddingLeft:var(--pf-global--spacer--3xl);--pf-c-login__main-footer-links-item--PaddingRight:var(--pf-global--spacer--md);--pf-c-login__main-footer-links-item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-login__main-footer-links-item--MarginBottom:var(--pf-global--spacer--sm);--pf-c-login__main-footer-links-item-link-svg--Fill:var(--pf-global--icon--Color--light);--pf-c-login__main-footer-links-item-link-svg--Width:var(--pf-global--icon--FontSize--lg);--pf-c-login__main-footer-links-item-link-svg--Height:var(--pf-global--icon--FontSize--lg);--pf-c-login__main-footer-links-item-link-svg--hover--Fill:var(--pf-global--icon--Color--dark);--pf-c-login__main-footer-band--PaddingTop:var(--pf-global--spacer--lg);--pf-c-login__main-footer-band--PaddingRight:var(--pf-global--spacer--md);--pf-c-login__main-footer-band--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-login__main-footer-band--PaddingLeft:var(--pf-global--spacer--md);--pf-c-login__main-footer-band--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-login__main-footer-band-item--PaddingTop:var(--pf-global--spacer--md);--pf-c-login__footer--PaddingLeft:var(--pf-global--spacer--md);--pf-c-login__footer--PaddingRight:var(--pf-global--spacer--md);--pf-c-login__footer--c-list--PaddingTop:var(--pf-global--spacer--md);--pf-c-login__footer--c-list--xl--PaddingTop:var(--pf-global--spacer--2xl);display:flex;justify-content:center;min-height:100vh;padding-top:var(--pf-c-login--PaddingTop);padding-bottom:var(--pf-c-login--PaddingBottom)}@media (min-width:1200px){.pf-c-login{--pf-c-login__container--MaxWidth:var(--pf-c-login__container--xl--MaxWidth)}}@media (min-width:576px){.pf-c-login{--pf-c-login__header--PaddingRight:0;--pf-c-login__header--PaddingLeft:0}}@media (min-width:1200px){.pf-c-login{--pf-c-login__header--MarginBottom:var(--pf-c-login__header--xl--MarginBottom);--pf-c-login__header--c-brand--MarginBottom:var(--pf-c-login__header--c-brand--xl--MarginBottom);--pf-c-login__main--MarginBottom:0}}@media (min-width:768px){.pf-c-login{--pf-c-login__main-header--PaddingRight:var(--pf-c-login__main-header--md--PaddingRight);--pf-c-login__main-header--PaddingLeft:var(--pf-c-login__main-header--md--PaddingLeft);--pf-c-login__main-header-desc--MarginBottom:var(--pf-c-login__main-header-desc--md--MarginBottom);--pf-c-login__main-body--PaddingRight:var(--pf-c-login__main-body--md--PaddingRight);--pf-c-login__main-body--PaddingLeft:var(--pf-c-login__main-body--md--PaddingLeft)}}@media (min-width:576px){.pf-c-login{--pf-c-login__footer--PaddingRight:0;--pf-c-login__footer--PaddingLeft:0}}@media (min-width:1200px){.pf-c-login{--pf-c-login__footer--c-list--PaddingTop:var(--pf-c-login__footer--c-list--xl--PaddingTop);background-image:var(--pf-c-login--xl--BackgroundImage)}}@media (min-width:576px){.pf-c-login{align-items:center}}.pf-c-login__container{width:100%;max-width:var(--pf-c-login__container--MaxWidth)}@media (min-width:1200px){.pf-c-login__container{display:grid;justify-content:center;grid-column-gap:var(--pf-c-login__container--xl--GridColumnGap);grid-template-columns:var(--pf-c-login__container--xl--GridTemplateColumns);grid-template-areas:"main header" "main footer" "main .";padding-right:var(--pf-c-login__container--PaddingRight);padding-left:var(--pf-c-login__container--PaddingLeft)}}.pf-c-login__header{color:var(--pf-global--Color--100);grid-area:header;padding-right:var(--pf-c-login__header--PaddingRight);padding-left:var(--pf-c-login__header--PaddingLeft)}@media (min-width:1200px){.pf-c-login__header{margin-top:var(--pf-c-login__header--xl--MarginTop)}}.pf-c-login__header .pf-c-brand{margin-bottom:var(--pf-c-login__header--c-brand--MarginBottom)}.pf-c-login__main{margin-bottom:var(--pf-c-login__main--MarginBottom);background-color:var(--pf-c-login__main--BackgroundColor);grid-area:main}.pf-c-login__main>:first-child:not(.pf-c-login__main-header){padding-top:var(--pf-c-login__main-header--PaddingTop)}.pf-c-login__main>:last-child:not(.pf-c-login__main-footer){padding-bottom:var(--pf-c-login__main-footer--PaddingBottom)}.pf-c-login__main-header{display:grid;grid-template-columns:100%;column-gap:var(--pf-c-login__main-header--ColumnGap);row-gap:var(--pf-c-login__main-header--RowGap);align-items:center;padding:var(--pf-c-login__main-header--PaddingTop) var(--pf-c-login__main-header--PaddingRight) var(--pf-c-login__main-header--PaddingBottom) var(--pf-c-login__main-header--PaddingLeft)}@media (min-width:768px){.pf-c-login__main-header{grid-template-columns:1fr auto}}.pf-c-login__main-header .pf-c-dropdown{grid-column:auto;grid-row:auto}@media (min-width:768px){.pf-c-login__main-header .pf-c-dropdown{grid-column:2/3;grid-row:1}}.pf-c-login__main-header-desc{margin-bottom:var(--pf-c-login__main-header-desc--MarginBottom);font-size:var(--pf-c-login__main-header-desc--FontSize);grid-column:1/-1}.pf-c-login__main-body{padding-right:var(--pf-c-login__main-body--PaddingRight);padding-bottom:var(--pf-c-login__main-body--PaddingBottom);padding-left:var(--pf-c-login__main-body--PaddingLeft)}.pf-c-login__main-footer{display:flex;flex-wrap:wrap}.pf-c-login__main-footer .pf-c-title{margin-bottom:var(--pf-c-login__main-footer--c-title--MarginBottom);text-align:center}.pf-c-login__main-footer>*{flex-basis:100%}.pf-c-login__main-footer-links{display:flex;flex-wrap:wrap;justify-content:center;padding:var(--pf-c-login__main-footer-links--PaddingTop) var(--pf-c-login__main-footer-links--PaddingRight) var(--pf-c-login__main-footer-links--PaddingBottom) var(--pf-c-login__main-footer-links--PaddingLeft)}.pf-c-login__main-footer-links-item{padding-right:var(--pf-c-login__main-footer-links-item--PaddingRight);padding-left:var(--pf-c-login__main-footer-links-item--PaddingLeft);margin-bottom:var(--pf-c-login__main-footer-links-item--MarginBottom)}.pf-c-login__main-footer-links-item-link svg{fill:var(--pf-c-login__main-footer-links-item-link-svg--Fill);width:100%;max-width:var(--pf-c-login__main-footer-links-item-link-svg--Width);height:100%;max-height:var(--pf-c-login__main-footer-links-item-link-svg--Height)}.pf-c-login__main-footer-links-item-link:hover svg{fill:var(--pf-c-login__main-footer-links-item-link-svg--hover--Fill)}.pf-c-login__main-footer-band{padding:var(--pf-c-login__main-footer-band--PaddingTop) var(--pf-c-login__main-footer-band--PaddingRight) var(--pf-c-login__main-footer-band--PaddingBottom) var(--pf-c-login__main-footer-band--PaddingLeft);text-align:center;background-color:var(--pf-c-login__main-footer-band--BackgroundColor)}.pf-c-login__main-footer-band>*+*{padding-top:var(--pf-c-login__main-footer-band-item--PaddingTop)}.pf-c-login__footer{color:var(--pf-global--Color--100);grid-area:footer;padding-right:var(--pf-c-login__footer--PaddingRight);padding-left:var(--pf-c-login__footer--PaddingLeft)}.pf-c-login__footer .pf-c-list a{color:unset}.pf-c-login__footer .pf-c-list:not(:only-child){padding-top:var(--pf-c-login__footer--c-list--PaddingTop)}.pf-c-menu{color:var(--pf-global--Color--100);--pf-c-menu--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-menu--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-menu--PaddingTop:var(--pf-global--spacer--sm);--pf-c-menu--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-menu--m-flyout__menu--Top:calc(var(--pf-c-menu--PaddingTop)*-1);--pf-c-menu--c-divider--MarginTop:var(--pf-global--spacer--sm);--pf-c-menu--c-divider--MarginBottom:var(--pf-global--spacer--sm);--pf-c-menu__search--PaddingTop:var(--pf-global--spacer--sm);--pf-c-menu__search--PaddingRight:var(--pf-global--spacer--md);--pf-c-menu__search--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-menu__search--PaddingLeft:var(--pf-global--spacer--md);--pf-c-menu__list-item--Color:var(--pf-global--Color--100);--pf-c-menu__list-item--hover--Color:var(--pf-global--Color--100);--pf-c-menu__list-item--BackgroundColor:transparent;--pf-c-menu__list-item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-menu__item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-menu__item--PaddingRight:var(--pf-global--spacer--md);--pf-c-menu__item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-menu__item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-menu__item--OutlineOffset:-0.125rem;--pf-c-menu__item--FontSize:var(--pf-global--FontSize--md);--pf-c-menu__item--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-menu__item--LineHeight:var(--pf-global--LineHeight--md);--pf-c-menu__item--disabled--Color:var(--pf-global--Color--dark-200);--pf-c-menu__group-title--PaddingTop:var(--pf-c-menu__item--PaddingTop);--pf-c-menu__group-title--PaddingRight:var(--pf-c-menu__item--PaddingRight);--pf-c-menu__group-title--PaddingBottom:var(--pf-c-menu__item--PaddingBottom);--pf-c-menu__group-title--PaddingLeft:var(--pf-c-menu__item--PaddingLeft);--pf-c-menu__group-title--FontSize:var(--pf-global--FontSize--sm);--pf-c-menu__group-title--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-menu__group-title--Color:var(--pf-global--Color--dark-200);--pf-c-menu__item-description--FontSize:var(--pf-global--FontSize--xs);--pf-c-menu__item-description--Color:var(--pf-global--Color--200);--pf-c-menu__item-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-menu__item-toggle-icon--PaddingRight:var(--pf-global--spacer--sm);--pf-c-menu__item-toggle-icon--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-menu__item-text--item-toggle-icon--MarginLeft:var(--pf-global--spacer--sm);--pf-c-menu__item-toggle-icon--item-text--MarginLeft:var(--pf-global--spacer--sm);--pf-c-menu__item-select-icon--MarginLeft:var(--pf-global--spacer--sm);--pf-c-menu__item-select-icon--Color:var(--pf-global--active-color--100);--pf-c-menu__item-select-icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-menu__item-main__external-icon--MarginLeft:var(--pf-global--spacer--sm);--pf-c-menu__item-main__external-icon--Color:var(--pf-global--link--Color);--pf-c-menu__item-main__external-icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-menu__item-action--PaddingTop:var(--pf-global--spacer--sm);--pf-c-menu__item-action--PaddingRight:var(--pf-global--spacer--md);--pf-c-menu__item-action--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-menu__item-action--PaddingLeft:var(--pf-global--spacer--md);--pf-c-menu__item-action-icon--Color:var(--pf-global--Color--dark-200);--pf-c-menu__item-action-icon--Height:calc(var(--pf-c-menu__item--FontSize)*var(--pf-c-menu__item--LineHeight));--pf-c-menu__item-action--hover__icon--Color:var(--pf-global--Color--dark-100);--pf-c-menu__item-action--m-favorite__icon--Color:var(--pf-global--disabled-color--200);--pf-c-menu__item-action--m-favorite__icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-menu__item-action--m-favorite--m-favorited__icon--Color:var(--pf-global--palette--gold-400);--pf-c-menu--m-drilldown--Width:auto;--pf-c-menu--m-drilldown--Height:auto;--pf-c-menu--m-drilldown--TransitionDuration--transform:var(--pf-global--TransitionDuration);--pf-c-menu--m-drilldown--TransitionDuration--height:var(--pf-global--TransitionDuration);--pf-c-menu--m-drilldown--Transition:transform var(--pf-c-menu--m-drilldown--TransitionDuration--transform),height var(--pf-c-menu--m-drilldown--TransitionDuration--height);--pf-c-menu--m-drilldown--c-menu--Top:calc(var(--pf-c-menu--PaddingTop)*-1);--pf-c-menu--m-drilldown--c-menu--TransitionDuration--transform:var(--pf-global--TransitionDuration);--pf-c-menu--m-drilldown--c-menu--TransitionDuration--visibility:var(--pf-global--TransitionDuration);--pf-c-menu--m-drilldown--c-menu--Transition:transform var(--pf-c-menu--m-drilldown--c-menu--TransitionDuration--transform),visibility var(--pf-c-menu--m-drilldown--c-menu--TransitionDuration--visibility);--pf-c-menu--m-drilldown__list--TransitionDuration--transform:var(--pf-global--TransitionDuration);--pf-c-menu--m-drilldown__list--Transition:transform var(--pf-c-menu--m-drilldown__list--TransitionDuration--transform);--pf-c-menu--m-drilled-in--c-menu__list-item--m-current-path--c-menu--ZIndex:var(--pf-global--ZIndex--2xl);padding-top:var(--pf-c-menu--PaddingTop);padding-bottom:var(--pf-c-menu--PaddingBottom);background-color:var(--pf-c-menu--BackgroundColor);box-shadow:var(--pf-c-menu--BoxShadow)}.pf-c-menu.pf-m-flyout .pf-c-menu{position:absolute;top:var(--pf-c-menu--m-flyout__menu--Top);left:100%}.pf-c-menu.pf-m-flyout .pf-c-menu__list-item{position:relative}.pf-c-menu.pf-m-drilldown{width:var(--pf-c-menu--m-drilldown--Width);height:var(--pf-c-menu--m-drilldown--Height);overflow:hidden;transition:var(--pf-c-menu--m-drilldown--Transition)}.pf-c-menu.pf-m-drilldown.pf-m-drilled-in>.pf-c-menu__content>.pf-c-menu__list,.pf-c-menu.pf-m-drilldown.pf-m-drilled-in>.pf-c-menu__list{transform:translateX(-100%)}.pf-c-menu.pf-m-drilldown .pf-c-menu{--pf-c-menu--BoxShadow:none;position:absolute;top:var(--pf-c-menu--m-drilldown--c-menu--Top);left:100%;width:100%;transition:var(--pf-c-menu--m-drilldown--c-menu--Transition)}.pf-c-menu.pf-m-drilldown .pf-c-menu.pf-m-drilled-in{transform:translateX(-100%)}.pf-c-menu.pf-m-drilldown .pf-c-menu__list{position:relative;transition:var(--pf-c-menu--m-drilldown__list--Transition)}.pf-c-menu.pf-m-drilldown .pf-c-menu__list-item.pf-m-current-path .pf-c-menu{z-index:var(--pf-c-menu--m-drilled-in--c-menu__list-item--m-current-path--c-menu--ZIndex)}.pf-c-menu.pf-m-drilldown .pf-c-menu__list-item:not(.pf-m-current-path) .pf-c-menu{visibility:hidden}.pf-c-menu.pf-m-drilldown .pf-c-menu__item{outline-offset:var(--pf-c-menu__item--OutlineOffset)}.pf-c-menu .pf-c-divider{margin-top:var(--pf-c-menu--c-divider--MarginTop);margin-bottom:var(--pf-c-menu--c-divider--MarginBottom)}.pf-c-menu__search{padding:var(--pf-c-menu__search--PaddingTop) var(--pf-c-menu__search--PaddingRight) var(--pf-c-menu__search--PaddingBottom) var(--pf-c-menu__search--PaddingLeft)}.pf-c-menu__list-item{display:flex;color:var(--pf-c-menu__list-item--Color);background-color:var(--pf-c-menu__list-item--BackgroundColor)}.pf-c-menu__list-item:focus-within:not(.pf-m-disabled),.pf-c-menu__list-item:hover:not(.pf-m-disabled){--pf-c-menu__list-item--Color:var(--pf-c-menu__list-item--hover--Color);--pf-c-menu__list-item--BackgroundColor:var(--pf-c-menu__list-item--hover--BackgroundColor)}.pf-c-menu__list-item:focus-within:not(.pf-m-disabled) .pf-c-menu__item-external-icon,.pf-c-menu__list-item:hover:not(.pf-m-disabled) .pf-c-menu__item-external-icon{opacity:1}.pf-c-menu__list-item.pf-m-disabled .pf-c-menu__item{--pf-c-menu__item--Color:var(--pf-c-menu__item--disabled--Color);pointer-events:none}.pf-c-menu__item{display:flex;flex-basis:100%;flex-direction:column;min-width:0;padding:var(--pf-c-menu__item--PaddingTop) var(--pf-c-menu__item--PaddingRight) var(--pf-c-menu__item--PaddingBottom) var(--pf-c-menu__item--PaddingLeft);font-size:var(--pf-c-menu__item--FontSize);font-weight:var(--pf-c-menu__item--FontWeight);line-height:var(--pf-c-menu__item--LineHeight);color:var(--pf-c-menu__item--Color);text-align:left;background-color:var(--pf-c-menu__item--BackgroundColor);border:none}.pf-c-menu__item:hover{text-decoration:none}.pf-c-menu__item:disabled{--pf-c-menu__item--Color:var(--pf-c-menu__item--disabled--Color);pointer-events:none}.pf-c-menu__item.pf-m-selected .pf-c-menu__item-select-icon{opacity:1}.pf-c-menu__item-main{display:flex;align-items:center;width:100%}.pf-c-menu__item-main .pf-c-menu__item-external-icon{margin-left:var(--pf-c-menu__item-main__external-icon--MarginLeft);font-size:var(--pf-c-menu__item-main__external-icon--FontSize);color:var(--pf-c-menu__item-main__external-icon--Color);opacity:0}.pf-c-menu__item-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-grow:1}.pf-c-menu__group-title{padding:var(--pf-c-menu__group-title--PaddingTop) var(--pf-c-menu__group-title--PaddingRight) var(--pf-c-menu__group-title--PaddingBottom) var(--pf-c-menu__group-title--PaddingLeft);font-size:var(--pf-c-menu__group-title--FontSize);font-weight:var(--pf-c-menu__group-title--FontWeight);color:var(--pf-c-menu__group-title--Color)}.pf-c-menu__item-description{font-size:var(--pf-c-menu__item-description--FontSize);color:var(--pf-c-menu__item-description--Color);word-break:break-all}.pf-c-menu__item-icon{margin-right:var(--pf-c-menu__item-icon--MarginRight)}.pf-c-menu__item-toggle-icon{padding-right:var(--pf-c-menu__item-toggle-icon--PaddingRight);padding-left:var(--pf-c-menu__item-toggle-icon--PaddingLeft)}.pf-c-menu__item-text+.pf-c-menu__item-toggle-icon{margin-left:var(--pf-c-menu__item-text--item-toggle-icon--MarginLeft)}.pf-c-menu__item-toggle-icon+.pf-c-menu__item-text{margin-left:var(--pf-c-menu__item-toggle-icon--item-text--MarginLeft)}.pf-c-menu__item-select-icon{margin-left:var(--pf-c-menu__item-select-icon--MarginLeft);font-size:var(--pf-c-menu__item-select-icon--FontSize);color:var(--pf-c-menu__item-select-icon--Color);opacity:0}.pf-c-menu__item-action{display:flex;padding:var(--pf-c-menu__item-action--PaddingTop) var(--pf-c-menu__item-action--PaddingRight) var(--pf-c-menu__item-action--PaddingBottom) var(--pf-c-menu__item-action--PaddingLeft);border:none}.pf-c-menu__item-action:focus,.pf-c-menu__item-action:hover{--pf-c-menu__item-action-icon--Color:var(--pf-c-menu__item-action--hover__icon--Color)}.pf-c-menu__item-action.pf-m-favorite{--pf-c-menu__item-action-icon--Color:var(--pf-c-menu__item-action--m-favorite__icon--Color)}.pf-c-menu__item-action.pf-m-favorite.pf-m-favorited{--pf-c-menu__item-action-icon--Color:var(--pf-c-menu__item-action--m-favorite--m-favorited__icon--Color)}.pf-c-menu__item-action.pf-m-favorite .pf-c-menu__item-action-icon{font-size:var(--pf-c-menu__item-action--m-favorite__icon--FontSize)}.pf-c-menu__item-action-icon{display:flex;align-items:center;height:var(--pf-c-menu__item-action-icon--Height);color:var(--pf-c-menu__item-action-icon--Color)}.pf-c-modal-box{--pf-c-modal-box--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-modal-box--BoxShadow:var(--pf-global--BoxShadow--xl);--pf-c-modal-box--ZIndex:var(--pf-global--ZIndex--xl);--pf-c-modal-box--Width:100%;--pf-c-modal-box--MaxWidth:calc(100% - var(--pf-global--spacer--xl));--pf-c-modal-box--m-sm--sm--MaxWidth:35rem;--pf-c-modal-box--m-md--Width:52.5rem;--pf-c-modal-box--m-lg--lg--MaxWidth:70rem;--pf-c-modal-box--MaxHeight:calc(100% - var(--pf-global--spacer--2xl));--pf-c-modal-box--m-align-top--spacer:var(--pf-global--spacer--sm);--pf-c-modal-box--m-align-top--xl--spacer:var(--pf-global--spacer--xl);--pf-c-modal-box--m-align-top--MarginTop:var(--pf-c-modal-box--m-align-top--spacer);--pf-c-modal-box--m-align-top--MaxHeight:calc(100% - min(var(--pf-c-modal-box--m-align-top--spacer), var(--pf-global--spacer--2xl)) - var(--pf-c-modal-box--m-align-top--spacer));--pf-c-modal-box--m-align-top--MaxWidth:calc(100% - min(var(--pf-c-modal-box--m-align-top--spacer) * 2, var(--pf-global--spacer--xl)));--pf-c-modal-box--m-danger__title-icon--Color:var(--pf-global--danger-color--100);--pf-c-modal-box--m-warning__title-icon--Color:var(--pf-global--warning-color--100);--pf-c-modal-box--m-success__title-icon--Color:var(--pf-global--success-color--100);--pf-c-modal-box--m-info__title-icon--Color:var(--pf-global--info-color--100);--pf-c-modal-box--m-default__title-icon--Color:var(--pf-global--default-color--200);--pf-c-modal-box__header--PaddingTop:var(--pf-global--spacer--lg);--pf-c-modal-box__header--PaddingRight:var(--pf-global--spacer--lg);--pf-c-modal-box__header--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-modal-box__header--last-child--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-modal-box__title--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-modal-box__title--FontFamily:var(--pf-global--FontFamily--heading--sans-serif);--pf-c-modal-box__title--FontSize:var(--pf-global--FontSize--2xl);--pf-c-modal-box__title-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-modal-box__title-icon--Color:var(--pf-global--Color--100);--pf-c-modal-box__description--PaddingTop:var(--pf-global--spacer--xs);--pf-c-modal-box__body--MinHeight:calc(var(--pf-global--FontSize--md)*var(--pf-global--LineHeight--md));--pf-c-modal-box__body--PaddingTop:var(--pf-global--spacer--lg);--pf-c-modal-box__body--PaddingRight:var(--pf-global--spacer--lg);--pf-c-modal-box__body--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-modal-box__body--last-child--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-modal-box__header--body--PaddingTop:var(--pf-global--spacer--md);--pf-c-modal-box--c-button--Top:var(--pf-global--spacer--lg);--pf-c-modal-box--c-button--Right:var(--pf-global--spacer--md);--pf-c-modal-box--c-button--sibling--MarginRight:calc(var(--pf-global--spacer--xl) + var(--pf-global--spacer--sm));--pf-c-modal-box__footer--PaddingTop:var(--pf-global--spacer--lg);--pf-c-modal-box__footer--PaddingRight:var(--pf-global--spacer--lg);--pf-c-modal-box__footer--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-modal-box__footer--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-modal-box__footer--c-button--MarginRight:var(--pf-global--spacer--md);--pf-c-modal-box__footer--c-button--sm--MarginRight:calc(var(--pf-c-modal-box__footer--c-button--MarginRight)/2);position:relative;z-index:var(--pf-c-modal-box--ZIndex);display:flex;flex-direction:column;width:var(--pf-c-modal-box--Width);max-width:var(--pf-c-modal-box--MaxWidth);max-height:var(--pf-c-modal-box--MaxHeight);background-color:var(--pf-c-modal-box--BackgroundColor);box-shadow:var(--pf-c-modal-box--BoxShadow)}@media (min-width:1200px){.pf-c-modal-box{--pf-c-modal-box--m-align-top--spacer:var(--pf-c-modal-box--m-align-top--xl--spacer)}}.pf-c-modal-box.pf-m-sm{--pf-c-modal-box--Width:var(--pf-c-modal-box--m-sm--sm--MaxWidth)}.pf-c-modal-box.pf-m-md{--pf-c-modal-box--Width:var(--pf-c-modal-box--m-md--Width)}.pf-c-modal-box.pf-m-lg{--pf-c-modal-box--Width:var(--pf-c-modal-box--m-lg--lg--MaxWidth)}.pf-c-modal-box.pf-m-align-top{top:var(--pf-c-modal-box--m-align-top--MarginTop);align-self:flex-start;max-width:var(--pf-c-modal-box--m-align-top--MaxWidth);max-height:var(--pf-c-modal-box--m-align-top--MaxHeight)}.pf-c-modal-box.pf-m-danger{--pf-c-modal-box__title-icon--Color:var(--pf-c-modal-box--m-danger__title-icon--Color)}.pf-c-modal-box.pf-m-warning{--pf-c-modal-box__title-icon--Color:var(--pf-c-modal-box--m-warning__title-icon--Color)}.pf-c-modal-box.pf-m-success{--pf-c-modal-box__title-icon--Color:var(--pf-c-modal-box--m-success__title-icon--Color)}.pf-c-modal-box.pf-m-default{--pf-c-modal-box__title-icon--Color:var(--pf-c-modal-box--m-default__title-icon--Color)}.pf-c-modal-box.pf-m-info{--pf-c-modal-box__title-icon--Color:var(--pf-c-modal-box--m-info__title-icon--Color)}.pf-c-modal-box>.pf-c-button{position:absolute;top:var(--pf-c-modal-box--c-button--Top);right:var(--pf-c-modal-box--c-button--Right)}.pf-c-modal-box>.pf-c-button+*{margin-right:var(--pf-c-modal-box--c-button--sibling--MarginRight)}.pf-c-modal-box__header{display:flex;flex-direction:column;padding-top:var(--pf-c-modal-box__header--PaddingTop);padding-right:var(--pf-c-modal-box__header--PaddingRight);padding-left:var(--pf-c-modal-box__header--PaddingLeft)}.pf-c-modal-box__header.pf-m-help{display:flex;flex-direction:row}.pf-c-modal-box__header:last-child{padding-bottom:var(--pf-c-modal-box__header--last-child--PaddingBottom)}.pf-c-modal-box__header+.pf-c-modal-box__body{--pf-c-modal-box__body--PaddingTop:var(--pf-c-modal-box__header--body--PaddingTop)}.pf-c-modal-box__header-main{flex-grow:1;min-width:0}.pf-c-modal-box__title,.pf-c-modal-box__title-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-c-modal-box__title{flex:0 0 auto;font-family:var(--pf-c-modal-box__title--FontFamily);font-size:var(--pf-c-modal-box__title--FontSize);line-height:var(--pf-c-modal-box__title--LineHeight)}.pf-c-modal-box__title.pf-m-icon{display:flex}.pf-c-modal-box__title-icon{margin-right:var(--pf-c-modal-box__title-icon--MarginRight);color:var(--pf-c-modal-box__title-icon--Color)}.pf-c-modal-box__description{padding-top:var(--pf-c-modal-box__description--PaddingTop)}.pf-c-modal-box__body{flex:1 1 auto;min-height:var(--pf-c-modal-box__body--MinHeight);padding-top:var(--pf-c-modal-box__body--PaddingTop);padding-right:var(--pf-c-modal-box__body--PaddingRight);padding-left:var(--pf-c-modal-box__body--PaddingLeft);overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;word-break:break-word;-webkit-overflow-scrolling:touch}.pf-c-modal-box__body:last-child{padding-bottom:var(--pf-c-modal-box__body--last-child--PaddingBottom)}.pf-c-modal-box__footer{display:flex;flex:0 0 auto;align-items:center;padding:var(--pf-c-modal-box__footer--PaddingTop) var(--pf-c-modal-box__footer--PaddingRight) var(--pf-c-modal-box__footer--PaddingBottom) var(--pf-c-modal-box__footer--PaddingLeft)}.pf-c-modal-box__footer>.pf-c-button:not(:last-child){margin-right:var(--pf-c-modal-box__footer--c-button--MarginRight)}@media screen and (min-width:576px){.pf-c-modal-box__footer>.pf-c-button:not(:last-child){--pf-c-modal-box__footer--c-button--MarginRight:var(--pf-c-modal-box__footer--c-button--sm--MarginRight)}}.pf-c-nav{--pf-c-nav--Transition:var(--pf-global--Transition);--pf-c-nav__item--m-expanded__toggle-icon--Rotate:90deg;--pf-c-nav--m-light__item--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-nav--m-light__item--m-current--not--m-expanded__link--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-nav--m-light__link--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-light__link--hover--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-light__link--focus--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-light__link--active--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-light__link--m-current--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-light__link--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-nav--m-light__link--focus--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-nav--m-light__link--active--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-nav--m-light__link--m-current--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-nav--m-light__link--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-nav--m-light__link--after--BorderColor:var(--pf-global--active-color--100);--pf-c-nav--m-light__link--m-current--after--BorderColor:var(--pf-global--active-color--100);--pf-c-nav--m-light__section-title--Color:var(--pf-global--Color--dark-200);--pf-c-nav--m-light__section-title--BorderBottomColor:var(--pf-global--BorderColor--300);--pf-c-nav--m-light--c-divider--BackgroundColor:var(--pf-global--BorderColor--300);--pf-c-nav--m-light__subnav__link--hover--after--BorderColor:var(--pf-global--BorderColor--dark-100);--pf-c-nav--m-light__subnav__link--focus--after--BorderColor:var(--pf-global--BorderColor--dark-100);--pf-c-nav--m-light__subnav__link--active--after--BorderColor:var(--pf-global--BorderColor--dark-100);--pf-c-nav--m-light__subnav__link--m-current--after--BorderColor:var(--pf-global--active-color--100);--pf-c-nav__item--MarginTop:0;--pf-c-nav__item--m-current--not--m-expanded__link--BackgroundColor:var(--pf-global--BackgroundColor--dark-400);--pf-c-nav__link--m-current--not--m-expanded__link--after--BorderWidth:var(--pf-global--BorderWidth--xl);--pf-c-nav__item--before--BorderColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__item--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__link--FontSize:var(--pf-global--FontSize--md);--pf-c-nav__link--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-nav__link--PaddingTop:var(--pf-global--spacer--md);--pf-c-nav__link--PaddingRight:var(--pf-global--spacer--md);--pf-c-nav__link--PaddingBottom:var(--pf-global--spacer--md);--pf-c-nav__link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-nav__link--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-nav__link--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-nav__link--Color:var(--pf-global--Color--light-100);--pf-c-nav__link--hover--Color:var(--pf-global--Color--light-100);--pf-c-nav__link--focus--Color:var(--pf-global--Color--light-100);--pf-c-nav__link--active--Color:var(--pf-global--Color--light-100);--pf-c-nav__link--m-current--Color:var(--pf-global--Color--light-100);--pf-c-nav__link--BackgroundColor:transparent;--pf-c-nav__link--hover--BackgroundColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__link--focus--BackgroundColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__link--active--BackgroundColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__link--m-current--BackgroundColor:var(--pf-global--BackgroundColor--dark-400);--pf-c-nav__link--OutlineOffset:calc(var(--pf-global--spacer--xs)*-1);--pf-c-nav__link--before--BorderColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__link--before--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__link--hover--before--BorderBottomWidth:0;--pf-c-nav__link--focus--before--BorderBottomWidth:0;--pf-c-nav__link--active--before--BorderBottomWidth:0;--pf-c-nav__link--m-current--before--BorderBottomWidth:0;--pf-c-nav__link--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__link--hover--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__link--focus--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__link--active--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__link--m-current--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__link--after--BorderLeftWidth:0;--pf-c-nav__link--hover--after--BorderLeftWidth:0;--pf-c-nav__link--focus--after--BorderLeftWidth:0;--pf-c-nav__link--active--after--BorderLeftWidth:0;--pf-c-nav__link--m-current--after--BorderLeftWidth:var(--pf-global--BorderWidth--xl);--pf-c-nav--m-horizontal__link--PaddingTop:var(--pf-global--spacer--sm);--pf-c-nav--m-horizontal__link--PaddingRight:var(--pf-global--spacer--md);--pf-c-nav--m-horizontal__link--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-nav--m-horizontal__link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-nav--m-horizontal__link--lg--PaddingTop:var(--pf-global--spacer--lg);--pf-c-nav--m-horizontal__link--lg--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-nav--m-horizontal__link--Right:var(--pf-global--spacer--md);--pf-c-nav--m-horizontal__link--Left:var(--pf-global--spacer--md);--pf-c-nav--m-horizontal__link--Color:var(--pf-global--Color--light-300);--pf-c-nav--m-horizontal__link--hover--Color:var(--pf-global--active-color--400);--pf-c-nav--m-horizontal__link--focus--Color:var(--pf-global--active-color--400);--pf-c-nav--m-horizontal__link--active--Color:var(--pf-global--active-color--400);--pf-c-nav--m-horizontal__link--m-current--Color:var(--pf-global--active-color--400);--pf-c-nav--m-horizontal__link--BackgroundColor:transparent;--pf-c-nav--m-horizontal__link--hover--BackgroundColor:transparent;--pf-c-nav--m-horizontal__link--focus--BackgroundColor:transparent;--pf-c-nav--m-horizontal__link--active--BackgroundColor:transparent;--pf-c-nav--m-horizontal__link--m-current--BackgroundColor:transparent;--pf-c-nav--m-horizontal__link--before--BorderColor:var(--pf-global--active-color--400);--pf-c-nav--m-horizontal__link--before--BorderWidth:0;--pf-c-nav--m-horizontal__link--hover--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-horizontal__link--focus--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-horizontal__link--active--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-horizontal__link--m-current--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-tertiary__link--PaddingTop:var(--pf-global--spacer--sm);--pf-c-nav--m-tertiary__link--PaddingRight:var(--pf-global--spacer--md);--pf-c-nav--m-tertiary__link--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-nav--m-tertiary__link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-nav--m-tertiary__link--Right:var(--pf-global--spacer--md);--pf-c-nav--m-tertiary__link--Left:var(--pf-global--spacer--md);--pf-c-nav--m-tertiary__link--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-tertiary__link--hover--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__link--focus--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__link--active--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__link--m-current--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__link--BackgroundColor:transparent;--pf-c-nav--m-tertiary__link--hover--BackgroundColor:transparent;--pf-c-nav--m-tertiary__link--focus--BackgroundColor:transparent;--pf-c-nav--m-tertiary__link--active--BackgroundColor:transparent;--pf-c-nav--m-tertiary__link--m-current--BackgroundColor:transparent;--pf-c-nav--m-tertiary__link--before--BorderColor:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__link--before--BorderWidth:0;--pf-c-nav--m-tertiary__link--hover--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-tertiary__link--focus--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-tertiary__link--active--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-tertiary__link--m-current--before--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-nav--m-tertiary__scroll-button--Color:var(--pf-global--Color--dark-100);--pf-c-nav--m-tertiary__scroll-button--hover--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__scroll-button--focus--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__scroll-button--active--Color:var(--pf-global--active-color--100);--pf-c-nav--m-tertiary__scroll-button--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-nav--m-tertiary__scroll-button--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-nav--m-tertiary__scroll-button--disabled--before--BorderColor:var(--pf-global--disabled-color--300);--pf-c-nav__subnav--PaddingBottom:var(--pf-global--spacer--md);--pf-c-nav__subnav--xl--PaddingLeft:var(--pf-c-nav__link--PaddingLeft);--pf-c-nav__subnav__link--MarginTop:0;--pf-c-nav__subnav__link--PaddingTop:var(--pf-global--spacer--sm);--pf-c-nav__subnav__link--PaddingRight:var(--pf-global--spacer--lg);--pf-c-nav__subnav__link--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-nav__subnav__link--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-nav__subnav__link--FontSize:var(--pf-global--FontSize--sm);--pf-c-nav__subnav__link--hover--after--BorderColor:var(--pf-global--BorderColor--200);--pf-c-nav__subnav__link--focus--after--BorderColor:var(--pf-global--BorderColor--200);--pf-c-nav__subnav__link--active--after--BorderColor:var(--pf-global--BorderColor--200);--pf-c-nav__subnav__link--m-current--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__subnav__link--hover--after--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__subnav__link--focus--after--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__subnav__link--active--after--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__subnav__link--m-current--after--BorderWidth:var(--pf-global--BorderWidth--xl);--pf-c-nav__subnav--MaxHeight:0;--pf-c-nav__item--m-expanded__subnav--MaxHeight:100%;--pf-c-nav__subnav--c-divider--PaddingRight:var(--pf-global--spacer--lg);--pf-c-nav__subnav--c-divider--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-nav__section--MarginTop:var(--pf-global--spacer--sm);--pf-c-nav__section__item--MarginTop:var(--pf-global--spacer--sm);--pf-c-nav__section__link--PaddingTop:var(--pf-global--spacer--sm);--pf-c-nav__section__link--PaddingRight:var(--pf-global--spacer--md);--pf-c-nav__section__link--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-nav__section__link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-nav__section__link--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-nav__section__link--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-nav__section__link--FontSize:var(--pf-global--FontSize--md);--pf-c-nav__section__link--before--BorderBottomWidth:0;--pf-c-nav__section__link--hover--after--BorderColor:transparent;--pf-c-nav__section__link--focus--after--BorderColor:transparent;--pf-c-nav__section__link--active--after--BorderColor:transparent;--pf-c-nav__section__link--m-current--after--BorderColor:var(--pf-global--active-color--400);--pf-c-nav__section__link--hover--after--BorderWidth:0;--pf-c-nav__section__link--focus--after--BorderWidth:0;--pf-c-nav__section__link--active--after--BorderWidth:0;--pf-c-nav__section__link--m-current--after--BorderWidth:var(--pf-global--BorderWidth--xl);--pf-c-nav__section--section--MarginTop:var(--pf-global--spacer--xl);--pf-c-nav__section-title--PaddingTop:var(--pf-global--spacer--sm);--pf-c-nav__section-title--PaddingRight:var(--pf-global--spacer--md);--pf-c-nav__section-title--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-nav__section-title--PaddingLeft:var(--pf-global--spacer--md);--pf-c-nav__section-title--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-nav__section-title--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-nav__section-title--FontSize:var(--pf-global--FontSize--sm);--pf-c-nav__section-title--Color:var(--pf-global--Color--light-100);--pf-c-nav__section-title--BorderBottomColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__section-title--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__scroll-button--Color:var(--pf-global--Color--light-100);--pf-c-nav__scroll-button--hover--Color:var(--pf-global--active-color--400);--pf-c-nav__scroll-button--focus--Color:var(--pf-global--active-color--400);--pf-c-nav__scroll-button--active--Color:var(--pf-global--active-color--400);--pf-c-nav__scroll-button--disabled--Color:var(--pf-global--disabled-color--100);--pf-c-nav__scroll-button--BackgroundColor:transparent;--pf-c-nav__scroll-button--Width:var(--pf-global--target-size--MinWidth);--pf-c-nav__scroll-button--OutlineOffset:calc(-1*var(--pf-global--spacer--xs));--pf-c-nav__scroll-button--Transition:margin .125s,transform .125s,opacity .125s;--pf-c-nav__scroll-button--before--BorderColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-nav__scroll-button--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-nav__scroll-button--before--BorderRightWidth:0;--pf-c-nav__scroll-button--before--BorderLeftWidth:0;--pf-c-nav__scroll-button--disabled--before--BorderColor:transparent;--pf-c-nav__toggle--PaddingRight:var(--pf-global--spacer--sm);--pf-c-nav__toggle--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-nav__toggle--FontSize:var(--pf-global--icon--FontSize--md);--pf-c-nav__toggle-icon--Transition:var(--pf-global--TransitionDuration);--pf-c-nav--c-divider--MarginTop:var(--pf-global--spacer--sm);--pf-c-nav--c-divider--MarginBottom:var(--pf-global--spacer--sm);--pf-c-nav--c-divider--PaddingRight:0;--pf-c-nav--c-divider--PaddingLeft:0;--pf-c-nav--c-divider--BackgroundColor:var(--pf-global--BackgroundColor--dark-200)}@media screen and (min-width:1200px){.pf-c-nav{--pf-c-nav__link--PaddingRight:var(--pf-c-nav__link--xl--PaddingRight);--pf-c-nav__link--PaddingLeft:var(--pf-c-nav__link--xl--PaddingLeft);--pf-c-nav__section__link--PaddingRight:var(--pf-c-nav__section__link--xl--PaddingRight);--pf-c-nav__section__link--PaddingLeft:var(--pf-c-nav__section__link--xl--PaddingLeft);--pf-c-nav__section-title--PaddingRight:var(--pf-c-nav__section-title--xl--PaddingRight);--pf-c-nav__section-title--PaddingLeft:var(--pf-c-nav__section-title--xl--PaddingLeft);--pf-c-nav__subnav--PaddingLeft:var(--pf-c-nav__subnav--xl--PaddingLeft)}}.pf-c-nav.pf-m-horizontal,.pf-c-nav.pf-m-tertiary{overflow:hidden}.pf-c-nav.pf-m-horizontal,.pf-c-nav.pf-m-horizontal .pf-c-nav__list,.pf-c-nav.pf-m-tertiary,.pf-c-nav.pf-m-tertiary .pf-c-nav__list{position:relative;display:flex}.pf-c-nav.pf-m-horizontal .pf-c-nav__list,.pf-c-nav.pf-m-tertiary .pf-c-nav__list{flex:1;max-width:100%;overflow-x:auto;white-space:nowrap;-webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:-ms-autohiding-scrollbar}.pf-c-nav.pf-m-horizontal .pf-c-nav__list::-webkit-scrollbar,.pf-c-nav.pf-m-tertiary .pf-c-nav__list::-webkit-scrollbar{display:none}.pf-c-nav.pf-m-horizontal .pf-c-nav__item,.pf-c-nav.pf-m-tertiary .pf-c-nav__item{display:flex}.pf-c-nav.pf-m-horizontal .pf-c-nav__link,.pf-c-nav.pf-m-tertiary .pf-c-nav__link{align-items:center;align-self:stretch;white-space:nowrap}.pf-c-nav.pf-m-horizontal .pf-c-nav__link:before,.pf-c-nav.pf-m-tertiary .pf-c-nav__link:before{top:auto;bottom:0}.pf-c-nav.pf-m-horizontal .pf-c-nav__link:after,.pf-c-nav.pf-m-tertiary .pf-c-nav__link:after{content:none}.pf-c-nav.pf-m-horizontal .pf-c-nav__link:before{right:var(--pf-c-nav--m-horizontal__link--Right);left:var(--pf-c-nav--m-horizontal__link--Left)}.pf-c-nav.pf-m-tertiary .pf-c-nav__link:before{right:var(--pf-c-nav--m-tertiary__link--Right);left:var(--pf-c-nav--m-tertiary__link--Left)}.pf-c-nav.pf-m-light{--pf-c-nav__item--before--BorderColor:var(--pf-c-nav--m-light__item--before--BorderColor);--pf-c-nav__item--m-current--not--m-expanded__link--BackgroundColor:var(--pf-c-nav--m-light__item--m-current--not--m-expanded__link--BackgroundColor);--pf-c-nav__link--Color:var(--pf-c-nav--m-light__link--Color);--pf-c-nav__link--hover--Color:var(--pf-c-nav--m-light__link--hover--Color);--pf-c-nav__link--focus--Color:var(--pf-c-nav--m-light__link--focus--Color);--pf-c-nav__link--active--Color:var(--pf-c-nav--m-light__link--active--Color);--pf-c-nav__link--m-current--Color:var(--pf-c-nav--m-light__link--m-current--Color);--pf-c-nav__link--hover--BackgroundColor:var(--pf-c-nav--m-light__link--hover--BackgroundColor);--pf-c-nav__link--focus--BackgroundColor:var(--pf-c-nav--m-light__link--focus--BackgroundColor);--pf-c-nav__link--active--BackgroundColor:var(--pf-c-nav--m-light__link--active--BackgroundColor);--pf-c-nav__link--m-current--BackgroundColor:var(--pf-c-nav--m-light__link--m-current--BackgroundColor);--pf-c-nav__link--before--BorderColor:var(--pf-c-nav--m-light__link--before--BorderColor);--pf-c-nav__link--after--BorderColor:var(--pf-c-nav--m-light__link--after--BorderColor);--pf-c-nav__link--m-current--after--BorderColor:var(--pf-c-nav--m-light__link--m-current--after--BorderColor);--pf-c-nav__subnav__link--hover--after--BorderColor:var(--pf-c-nav--m-light__subnav__link--hover--after--BorderColor);--pf-c-nav__subnav__link--focus--after--BorderColor:var(--pf-c-nav--m-light__subnav__link--focus--after--BorderColor);--pf-c-nav__subnav__link--active--after--BorderColor:var(--pf-c-nav--m-light__subnav__link--active--after--BorderColor);--pf-c-nav__subnav__link--m-current--after--BorderColor:var(--pf-c-nav--m-light__subnav__link--m-current--after--BorderColor);--pf-c-nav__section-title--Color:var(--pf-c-nav--m-light__section-title--Color);--pf-c-nav__section-title--BorderBottomColor:var(--pf-c-nav--m-light__section-title--BorderBottomColor)}.pf-c-nav.pf-m-light .pf-c-divider{--pf-c-divider--after--BackgroundColor:var(--pf-c-nav--m-light--c-divider--BackgroundColor)}.pf-c-nav.pf-m-horizontal{--pf-c-nav__link--PaddingTop:var(--pf-c-nav--m-horizontal__link--PaddingTop);--pf-c-nav__link--PaddingRight:var(--pf-c-nav--m-horizontal__link--PaddingRight);--pf-c-nav__link--PaddingBottom:var(--pf-c-nav--m-horizontal__link--PaddingBottom);--pf-c-nav__link--PaddingLeft:var(--pf-c-nav--m-horizontal__link--PaddingLeft);--pf-c-nav__link--Right:var(--pf-c-nav--m-horizontal__link--Right);--pf-c-nav__link--Left:var(--pf-c-nav--m-horizontal__link--Left);--pf-c-nav__link--Color:var(--pf-c-nav--m-horizontal__link--Color);--pf-c-nav__link--hover--Color:var(--pf-c-nav--m-horizontal__link--hover--Color);--pf-c-nav__link--active--Color:var(--pf-c-nav--m-horizontal__link--active--Color);--pf-c-nav__link--focus--Color:var(--pf-c-nav--m-horizontal__link--focus--Color);--pf-c-nav__link--m-current--Color:var(--pf-c-nav--m-horizontal__link--m-current--Color);--pf-c-nav__link--BackgroundColor:var(--pf-c-nav--m-horizontal__link--BackgroundColor);--pf-c-nav__link--hover--BackgroundColor:var(--pf-c-nav--m-horizontal__link--hover--BackgroundColor);--pf-c-nav__link--focus--BackgroundColor:var(--pf-c-nav--m-horizontal__link--focus--BackgroundColor);--pf-c-nav__link--active--BackgroundColor:var(--pf-c-nav--m-horizontal__link--active--BackgroundColor);--pf-c-nav__link--m-current--BackgroundColor:var(--pf-c-nav--m-horizontal__link--m-current--BackgroundColor);--pf-c-nav__link--before--BorderColor:var(--pf-c-nav--m-horizontal__link--before--BorderColor);--pf-c-nav__link--before--BorderBottomWidth:var(--pf-c-nav--m-horizontal__link--before--BorderWidth);--pf-c-nav__link--hover--before--BorderBottomWidth:var(--pf-c-nav--m-horizontal__link--hover--before--BorderWidth);--pf-c-nav__link--focus--before--BorderBottomWidth:var(--pf-c-nav--m-horizontal__link--focus--before--BorderWidth);--pf-c-nav__link--active--before--BorderBottomWidth:var(--pf-c-nav--m-horizontal__link--active--before--BorderWidth);--pf-c-nav__link--m-current--before--BorderBottomWidth:var(--pf-c-nav--m-horizontal__link--m-current--before--BorderWidth)}.pf-c-nav.pf-m-tertiary{--pf-c-nav__link--PaddingTop:var(--pf-c-nav--m-tertiary__link--PaddingTop);--pf-c-nav__link--PaddingRight:var(--pf-c-nav--m-tertiary__link--PaddingRight);--pf-c-nav__link--PaddingBottom:var(--pf-c-nav--m-tertiary__link--PaddingBottom);--pf-c-nav__link--PaddingLeft:var(--pf-c-nav--m-tertiary__link--PaddingLeft);--pf-c-nav__link--Right:var(--pf-c-nav--m-tertiary__link--Right);--pf-c-nav__link--Left:var(--pf-c-nav--m-tertiary__link--Left);--pf-c-nav__link--Color:var(--pf-c-nav--m-tertiary__link--Color);--pf-c-nav__link--hover--Color:var(--pf-c-nav--m-tertiary__link--hover--Color);--pf-c-nav__link--active--Color:var(--pf-c-nav--m-tertiary__link--active--Color);--pf-c-nav__link--focus--Color:var(--pf-c-nav--m-tertiary__link--focus--Color);--pf-c-nav__link--m-current--Color:var(--pf-c-nav--m-tertiary__link--m-current--Color);--pf-c-nav__link--BackgroundColor:var(--pf-c-nav--m-tertiary__link--BackgroundColor);--pf-c-nav__link--hover--BackgroundColor:var(--pf-c-nav--m-tertiary__link--hover--BackgroundColor);--pf-c-nav__link--focus--BackgroundColor:var(--pf-c-nav--m-tertiary__link--focus--BackgroundColor);--pf-c-nav__link--active--BackgroundColor:var(--pf-c-nav--m-tertiary__link--active--BackgroundColor);--pf-c-nav__link--m-current--BackgroundColor:var(--pf-c-nav--m-tertiary__link--m-current--BackgroundColor);--pf-c-nav__link--before--BorderColor:var(--pf-c-nav--m-tertiary__link--before--BorderColor);--pf-c-nav__link--before--BorderBottomWidth:var(--pf-c-nav--m-tertiary__link--before--BorderWidth);--pf-c-nav__link--hover--before--BorderBottomWidth:var(--pf-c-nav--m-tertiary__link--hover--before--BorderWidth);--pf-c-nav__link--focus--before--BorderBottomWidth:var(--pf-c-nav--m-tertiary__link--focus--before--BorderWidth);--pf-c-nav__link--active--before--BorderBottomWidth:var(--pf-c-nav--m-tertiary__link--active--before--BorderWidth);--pf-c-nav__link--m-current--before--BorderBottomWidth:var(--pf-c-nav--m-tertiary__link--m-current--before--BorderWidth);--pf-c-nav__scroll-button--Color:var(--pf-c-nav--m-tertiary__scroll-button--Color);--pf-c-nav__scroll-button--hover--Color:var(--pf-c-nav--m-tertiary__scroll-button--hover--Color);--pf-c-nav__scroll-button--focus--Color:var(--pf-c-nav--m-tertiary__scroll-button--focus--Color);--pf-c-nav__scroll-button--active--Color:var(--pf-c-nav--m-tertiary__scroll-button--active--Color);--pf-c-nav__scroll-button--disabled--Color:var(--pf-c-nav--m-tertiary__scroll-button--disabled--Color);--pf-c-nav__scroll-button--before--BorderColor:var(--pf-c-nav--m-tertiary__scroll-button--before--BorderColor);--pf-c-nav__scroll-button--disabled--before--BorderColor:var(--pf-c-nav--m-tertiary__scroll-button--disabled--before--BorderColor)}.pf-c-nav .pf-c-divider{--pf-c-divider--after--BackgroundColor:var(--pf-c-nav--c-divider--BackgroundColor);padding-right:var(--pf-c-nav--c-divider--PaddingRight);padding-left:var(--pf-c-nav--c-divider--PaddingLeft);margin-top:var(--pf-c-nav--c-divider--MarginTop);margin-bottom:var(--pf-c-nav--c-divider--MarginBottom)}.pf-c-nav.pf-m-scrollable .pf-c-nav__scroll-button{opacity:1}.pf-c-nav.pf-m-scrollable .pf-c-nav__scroll-button:first-of-type{margin-right:0;transform:translateX(0)}.pf-c-nav.pf-m-scrollable .pf-c-nav__scroll-button:nth-of-type(2){margin-left:0;transform:translateX(0)}.pf-c-nav__list{display:block}.pf-c-nav__item{position:relative;margin-top:var(--pf-c-nav__item--MarginTop)}.pf-c-nav__item.pf-m-expandable{--pf-c-nav__link--before--BorderBottomWidth:0}.pf-c-nav__item.pf-m-expandable:before{position:absolute;right:0;bottom:calc(var(--pf-c-nav__item--before--BorderWidth)*-1);left:0;content:"";border-bottom:var(--pf-c-nav__item--before--BorderWidth) solid var(--pf-c-nav__item--before--BorderColor)}.pf-c-nav__link{position:relative;display:flex;align-items:baseline;padding:var(--pf-c-nav__link--PaddingTop) var(--pf-c-nav__link--PaddingRight) var(--pf-c-nav__link--PaddingBottom) var(--pf-c-nav__link--PaddingLeft);font-size:var(--pf-c-nav__link--FontSize);font-weight:var(--pf-c-nav__link--FontWeight);color:var(--pf-c-nav__link--Color);background-color:var(--pf-c-nav__link--BackgroundColor);outline-offset:var(--pf-c-nav__link--OutlineOffset)}.pf-c-nav__link:after,.pf-c-nav__link:before{position:absolute;content:"";border:0 solid}.pf-c-nav__link:before{right:0;bottom:calc(var(--pf-c-nav__link--before--BorderBottomWidth)*-1);left:0;border-color:var(--pf-c-nav__link--before--BorderColor);border-bottom-width:var(--pf-c-nav__link--before--BorderBottomWidth)}.pf-c-nav__link:after{top:0;bottom:0;left:0;border:0 solid;border-color:var(--pf-c-nav__link--after--BorderColor);border-left:var(--pf-c-nav__link--after--BorderLeftWidth) solid var(--pf-c-nav__link--after--BorderColor)}.pf-c-nav__link:hover{color:var(--pf-c-nav__link--hover--Color);background-color:var(--pf-c-nav__link--hover--BackgroundColor)}.pf-c-nav__link:hover:before{border-bottom-width:var(--pf-c-nav__link--hover--before--BorderBottomWidth)}.pf-c-nav__link:hover:after{border-color:var(--pf-c-nav__link--hover--after--BorderColor);border-left-width:var(--pf-c-nav__link--hover--after--BorderLeftWidth)}.pf-c-nav__link:focus{color:var(--pf-c-nav__link--focus--Color);background-color:var(--pf-c-nav__link--focus--BackgroundColor)}.pf-c-nav__link:focus:before{border-bottom-width:var(--pf-c-nav__link--focus--before--BorderBottomWidth)}.pf-c-nav__link:focus:after{border-color:var(--pf-c-nav__link--focus--after--BorderColor);border-left-width:var(--pf-c-nav__link--focus--after--BorderLeftWidth)}.pf-c-nav__link:active{color:var(--pf-c-nav__link--active--Color);background-color:var(--pf-c-nav__link--active--BackgroundColor)}.pf-c-nav__link:active:before{border-bottom-width:var(--pf-c-nav__link--active--before--BorderBottomWidth)}.pf-c-nav__link:active:after{border-color:var(--pf-c-nav__link--active--after--BorderColor);border-left-width:var(--pf-c-nav__link--active--after--BorderLeftWidth)}.pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link,.pf-c-nav__link.pf-m-current,.pf-c-nav__link.pf-m-current:hover{color:var(--pf-c-nav__link--m-current--Color);background-color:var(--pf-c-nav__link--m-current--BackgroundColor)}.pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link:before,.pf-c-nav__link.pf-m-current:before,.pf-c-nav__link.pf-m-current:hover:before{border-bottom-width:var(--pf-c-nav__link--m-current--before--BorderBottomWidth)}.pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link:after,.pf-c-nav__link.pf-m-current:after,.pf-c-nav__link.pf-m-current:hover:after{border-color:var(--pf-c-nav__link--m-current--after--BorderColor);border-left-width:var(--pf-c-nav__link--m-current--after--BorderLeftWidth)}.pf-c-nav__link,.pf-c-nav__link:active,.pf-c-nav__link:focus,.pf-c-nav__link:hover{width:100%;text-decoration:none;border:none}.pf-c-nav__subnav{--pf-c-nav__link--PaddingTop:var(--pf-c-nav__subnav__link--PaddingTop);--pf-c-nav__link--PaddingRight:var(--pf-c-nav__subnav__link--PaddingRight);--pf-c-nav__link--PaddingBottom:var(--pf-c-nav__subnav__link--PaddingBottom);--pf-c-nav__link--PaddingLeft:var(--pf-c-nav__subnav__link--PaddingLeft);--pf-c-nav__link--FontSize:var(--pf-c-nav__subnav__link--FontSize);--pf-c-nav__link--hover--after--BorderColor:var(--pf-c-nav__subnav__link--hover--after--BorderColor);--pf-c-nav__link--focus--after--BorderColor:var(--pf-c-nav__subnav__link--focus--after--BorderColor);--pf-c-nav__link--active--after--BorderColor:var(--pf-c-nav__subnav__link--active--after--BorderColor);--pf-c-nav__link--m-current--after--BorderColor:var(--pf-c-nav__subnav__link--m-current--after--BorderColor);--pf-c-nav__link--hover--after--BorderLeftWidth:var(--pf-c-nav__subnav__link--hover--after--BorderWidth);--pf-c-nav__link--focus--after--BorderLeftWidth:var(--pf-c-nav__subnav__link--focus--after--BorderWidth);--pf-c-nav__link--active--after--BorderLeftWidth:var(--pf-c-nav__subnav__link--active--after--BorderWidth);--pf-c-nav__link--m-current--after--BorderLeftWidth:var(--pf-c-nav__subnav__link--m-current--after--BorderWidth);--pf-c-nav--c-divider--PaddingRight:var(--pf-c-nav__subnav--c-divider--PaddingRight);--pf-c-nav--c-divider--PaddingLeft:var(--pf-c-nav__subnav--c-divider--PaddingLeft);max-height:var(--pf-c-nav__subnav--MaxHeight);padding-bottom:var(--pf-c-nav__subnav--PaddingBottom);padding-left:var(--pf-c-nav__subnav--PaddingLeft);transition:var(--pf-c-nav--Transition);scrollbar-width:none;-ms-overflow-style:-ms-autohiding-scrollbar}.pf-c-nav__item.pf-m-expanded .pf-c-nav__subnav{--pf-c-nav__subnav--MaxHeight:var(--pf-c-nav__item--m-expanded__subnav--MaxHeight);overflow-y:auto;opacity:1}.pf-c-nav__subnav::-webkit-scrollbar{display:none}.pf-c-nav__toggle{flex:none;padding-right:var(--pf-c-nav__toggle--PaddingRight);padding-left:var(--pf-c-nav__toggle--PaddingLeft);margin-left:auto;font-size:var(--pf-c-nav__toggle--FontSize);line-height:1}.pf-c-nav__toggle-icon{display:inline-block;transition:var(--pf-c-nav__toggle-icon--Transition)}.pf-c-nav__item.pf-m-expanded .pf-c-nav__toggle-icon{transform:rotate(var(--pf-c-nav__item--m-expanded__toggle-icon--Rotate))}.pf-c-nav__section{--pf-c-nav__item--MarginTop:var(--pf-c-nav__section__item--MarginTop);--pf-c-nav__link--PaddingTop:var(--pf-c-nav__section__link--PaddingTop);--pf-c-nav__link--PaddingRight:var(--pf-c-nav__section__link--PaddingRight);--pf-c-nav__link--PaddingBottom:var(--pf-c-nav__section__link--PaddingBottom);--pf-c-nav__link--PaddingLeft:var(--pf-c-nav__section__link--PaddingLeft);--pf-c-nav__link--FontSize:var(--pf-c-nav__section__link--FontSize);--pf-c-nav__link--before--BorderBottomWidth:var(--pf-c-nav__section__link--before--BorderBottomWidth);--pf-c-nav__link--hover--after--BorderColor:var(--pf-c-nav__section__link--hover--after--BorderColor);--pf-c-nav__link--focus--after--BorderColor:var(--pf-c-nav__section__link--focus--after--BorderColor);--pf-c-nav__link--active--after--BorderColor:var(--pf-c-nav__section__link--active--after--BorderColor);--pf-c-nav__link--m-current--after--BorderColor:var(--pf-c-nav__section__link--m-current--after--BorderColor);--pf-c-nav__link--hover--after--BorderLeftWidth:var(--pf-c-nav__section__link--hover--after--BorderWidth);--pf-c-nav__link--focus--after--BorderLeftWidth:var(--pf-c-nav__section__link--focus--after--BorderWidth);--pf-c-nav__link--active--after--BorderLeftWidth:var(--pf-c-nav__section__link--active--after--BorderWidth);--pf-c-nav__link--m-current--after--BorderLeftWidth:var(--pf-c-nav__section__link--m-current--after--BorderWidth);margin-top:var(--pf-c-nav__section--MarginTop);--pf-c-nav--c-divider--MarginBottom:0}.pf-c-nav__section+.pf-c-nav__section{--pf-c-nav__section--MarginTop:var(--pf-c-nav__section--section--MarginTop)}.pf-c-nav__section-title{padding:var(--pf-c-nav__section-title--PaddingTop) var(--pf-c-nav__section-title--PaddingRight) var(--pf-c-nav__section-title--PaddingBottom) var(--pf-c-nav__section-title--PaddingLeft);font-size:var(--pf-c-nav__section-title--FontSize);color:var(--pf-c-nav__section-title--Color);border-bottom:var(--pf-c-nav__section-title--BorderBottomWidth) solid var(--pf-c-nav__section-title--BorderBottomColor)}.pf-c-nav__scroll-button{flex:none;width:var(--pf-c-nav__scroll-button--Width);color:var(--pf-c-nav__scroll-button--Color);background-color:var(--pf-c-nav__scroll-button--BackgroundColor);border:0;outline-offset:var(--pf-c-nav__scroll-button--OutlineOffset);opacity:0;transition:var(--pf-c-nav__scroll-button--Transition)}.pf-c-nav__scroll-button:before{position:absolute;top:0;bottom:0;content:"";border:solid var(--pf-c-nav__scroll-button--before--BorderColor);border-left-width:var(--pf-c-nav__scroll-button--before--BorderLeftWidth);border-bottom-width:0;border-right-width:var(--pf-c-nav__scroll-button--before--BorderRightWidth);border-top-width:0}.pf-c-nav__scroll-button:hover{color:var(--pf-c-nav__scroll-button--hover--Color)}.pf-c-nav__scroll-button:focus{color:var(--pf-c-nav__scroll-button--focus--Color)}.pf-c-nav__scroll-button:active{color:var(--pf-c-nav__scroll-button--active--Color)}.pf-c-nav__scroll-button:disabled{color:var(--pf-c-nav__scroll-button--disabled--Color);border-color:var(--pf-c-nav__scroll-button--disabled--before--BorderColor)}.pf-c-nav__scroll-button:first-of-type{--pf-c-nav__scroll-button--before--BorderRightWidth:var(--pf-c-nav__scroll-button--before--BorderWidth);margin-right:calc(var(--pf-c-nav__scroll-button--Width)*-1);transform:translateX(-100%)}.pf-c-nav__scroll-button:first-of-type:before{right:0}.pf-c-nav__scroll-button:nth-of-type(2){--pf-c-nav__scroll-button--before--BorderLeftWidth:var(--pf-c-nav__scroll-button--before--BorderWidth);margin-left:calc(var(--pf-c-nav__scroll-button--Width)*-1);transform:translateX(100%)}.pf-c-nav__scroll-button:nth-of-type(2):before{left:0}.pf-c-notification-badge{--pf-c-notification-badge--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-notification-badge--PaddingRight:var(--pf-global--spacer--md);--pf-c-notification-badge--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-notification-badge--PaddingLeft:var(--pf-global--spacer--md);--pf-c-notification-badge--MarginTop:calc(-1*var(--pf-global--spacer--form-element));--pf-c-notification-badge--MarginRight:calc(-1*var(--pf-global--spacer--md));--pf-c-notification-badge--MarginBottom:calc(-1*var(--pf-global--spacer--form-element));--pf-c-notification-badge--MarginLeft:calc(-1*var(--pf-global--spacer--md));--pf-c-notification-badge--after--BorderColor:transparent;--pf-c-notification-badge--after--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-notification-badge--after--BorderWidth:0;--pf-c-notification-badge--after--Top:0;--pf-c-notification-badge--after--Right:0;--pf-c-notification-badge--after--Width:auto;--pf-c-notification-badge--after--Height:auto;--pf-c-notification-badge--after--BackgroundColor:transparent;--pf-c-notification-badge--after--TranslateX:0;--pf-c-notification-badge--after--TranslateY:0;--pf-c-notification-badge__i--Width:auto;--pf-c-notification-badge__i--Height:auto;--pf-c-notification-badge--m-read--after--BorderColor:transparent;--pf-c-notification-badge--m-read--after--BackgroundColor:transparent;--pf-c-notification-badge--m-unread--Color:var(--pf-global--Color--light-100);--pf-c-notification-badge--m-unread--after--BackgroundColor:var(--pf-global--active-color--100);--pf-c-notification-badge--m-unread--hover--after--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-notification-badge--m-attention--Color:var(--pf-global--Color--light-100);--pf-c-notification-badge--m-attention--after--BackgroundColor:var(--pf-global--danger-color--100);--pf-c-notification-badge--m-attention--hover--after--BackgroundColor:var(--pf-global--danger-color--200);--pf-c-notification-badge__count--MarginLeft:var(--pf-global--spacer--xs);--pf-c-notification-badge--pf-icon-attention-bell--LineHeight:var(--pf-global--LineHeight--sm);position:relative;display:inline-block;padding:var(--pf-c-notification-badge--PaddingTop) var(--pf-c-notification-badge--PaddingRight) var(--pf-c-notification-badge--PaddingBottom) var(--pf-c-notification-badge--PaddingLeft);margin:var(--pf-c-notification-badge--MarginTop) var(--pf-c-notification-badge--MarginRight) var(--pf-c-notification-badge--MarginBottom) var(--pf-c-notification-badge--MarginLeft);background-color:var(--pf-c-notification-badge--after--BackgroundColor);border-radius:var(--pf-c-notification-badge--after--BorderRadius)}.pf-c-notification-badge:before{position:absolute;top:var(--pf-c-notification-badge--after--Top);right:var(--pf-c-notification-badge--after--Right);bottom:0;left:0;width:var(--pf-c-notification-badge--after--Width);height:var(--pf-c-notification-badge--after--Height);content:"";border:var(--pf-c-notification-badge--after--BorderWidth) solid var(--pf-c-notification-badge--after--BorderColor);border-radius:var(--pf-c-notification-badge--after--BorderRadius);transform:translate(var(--pf-c-notification-badge--after--TranslateX),var(--pf-c-notification-badge--after--TranslateY))}.pf-c-notification-badge>i{width:var(--pf-c-notification-badge__i--Width);height:var(--pf-c-notification-badge__i--Height)}.pf-c-notification-badge>*{position:relative}.pf-c-notification-badge .pf-icon-attention-bell,.pf-c-notification-badge .pf-icon-bell{display:inline-block;line-height:var(--pf-c-notification-badge--pf-icon-attention-bell--LineHeight)}.pf-c-notification-badge .pf-icon-attention-bell:before,.pf-c-notification-badge .pf-icon-bell:before{vertical-align:bottom}.pf-c-notification-badge.pf-m-read{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-notification-badge--m-read--after--BackgroundColor);--pf-c-notification-badge--after--BorderColor:var(--pf-c-notification-badge--m-read--after--BorderColor)}.pf-c-notification-badge.pf-m-unread{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-notification-badge--m-unread--after--BackgroundColor);color:var(--pf-c-notification-badge--m-unread--Color)}.pf-c-notification-badge.pf-m-unread:hover{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-notification-badge--m-unread--hover--after--BackgroundColor)}.pf-c-notification-badge.pf-m-attention{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-notification-badge--m-attention--after--BackgroundColor);color:var(--pf-c-notification-badge--m-attention--Color)}.pf-c-notification-badge.pf-m-attention:hover{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-notification-badge--m-attention--hover--after--BackgroundColor)}.pf-c-notification-badge__count{margin-left:var(--pf-c-notification-badge__count--MarginLeft)}.pf-c-notification-drawer{--pf-c-notification-drawer--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-notification-drawer__header--PaddingTop:var(--pf-global--spacer--md);--pf-c-notification-drawer__header--PaddingRight:var(--pf-global--spacer--md);--pf-c-notification-drawer__header--PaddingBottom:var(--pf-global--spacer--md);--pf-c-notification-drawer__header--PaddingLeft:var(--pf-global--spacer--md);--pf-c-notification-drawer__header--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-notification-drawer__header--BoxShadow:var(--pf-global--BoxShadow--sm-bottom);--pf-c-notification-drawer__header--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-notification-drawer__header-title--FontSize:var(--pf-global--FontSize--xl);--pf-c-notification-drawer__header-status--MarginLeft:var(--pf-global--spacer--md);--pf-c-notification-drawer__body--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-notification-drawer__list-item--PaddingTop:var(--pf-global--spacer--md);--pf-c-notification-drawer__list-item--PaddingRight:var(--pf-global--spacer--md);--pf-c-notification-drawer__list-item--PaddingBottom:var(--pf-global--spacer--md);--pf-c-notification-drawer__list-item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-notification-drawer__list-item--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-notification-drawer__list-item--BoxShadow:inset var(--pf-global--BoxShadow--sm-bottom);--pf-c-notification-drawer__list-item--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-notification-drawer__list-item--BorderBottomColor:transparent;--pf-c-notification-drawer__list-item--OutlineOffset:-0.25rem;--pf-c-notification-drawer__list-item--before--Width:var(--pf-global--BorderWidth--lg);--pf-c-notification-drawer__list-item--before--Top:0;--pf-c-notification-drawer__list-item--before--Bottom:calc(var(--pf-c-notification-drawer__list-item--BorderBottomWidth)*-1);--pf-c-notification-drawer__list-item--m-info__list-item-header-icon--Color:var(--pf-global--info-color--100);--pf-c-notification-drawer__list-item--m-info__list-item--before--BackgroundColor:var(--pf-global--info-color--100);--pf-c-notification-drawer__list-item--m-warning__list-item-header-icon--Color:var(--pf-global--warning-color--100);--pf-c-notification-drawer__list-item--m-warning__list-item--before--BackgroundColor:var(--pf-global--warning-color--100);--pf-c-notification-drawer__list-item--m-danger__list-item-header-icon--Color:var(--pf-global--danger-color--100);--pf-c-notification-drawer__list-item--m-danger__list-item--before--BackgroundColor:var(--pf-global--danger-color--100);--pf-c-notification-drawer__list-item--m-success__list-item-header-icon--Color:var(--pf-global--success-color--100);--pf-c-notification-drawer__list-item--m-success__list-item--before--BackgroundColor:var(--pf-global--success-color--100);--pf-c-notification-drawer__list-item--m-default__list-item-header-icon--Color:var(--pf-global--default-color--200);--pf-c-notification-drawer__list-item--m-default__list-item--before--BackgroundColor:var(--pf-global--default-color--200);--pf-c-notification-drawer__list-item--m-read--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-notification-drawer__list-item--m-read--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-notification-drawer__list-item--m-read--before--Top:calc(var(--pf-c-notification-drawer__list-item--BorderBottomWidth)*-1);--pf-c-notification-drawer__list-item--m-read--before--Bottom:0;--pf-c-notification-drawer__list-item--m-read--before--BackgroundColor:transparent;--pf-c-notification-drawer__list-item--list-item--m-read--before--Top:0;--pf-c-notification-drawer__list-item--list-item--m-read--BoxShadow:inset var(--pf-global--BoxShadow--sm-bottom);--pf-c-notification-drawer__list-item--m-hoverable--hover--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-notification-drawer__list-item--m-hoverable--hover--BoxShadow:var(--pf-global--BoxShadow--md-top),var(--pf-global--BoxShadow--md-bottom);--pf-c-notification-drawer__list-item-header--MarginBottom:var(--pf-global--spacer--xs);--pf-c-notification-drawer__list-item-header-icon--Color:inherit;--pf-c-notification-drawer__list-item-header-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-notification-drawer__list-item-header-title--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-notification-drawer__list-item-header-title--max-lines:1;--pf-c-notification-drawer__list-item--m-read__list-item-header-title--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-notification-drawer__list-item-description--MarginBottom:var(--pf-global--spacer--sm);--pf-c-notification-drawer__list-item-timestamp--Color:var(--pf-global--Color--200);--pf-c-notification-drawer__list-item-timestamp--FontSize:var(--pf-global--FontSize--sm);--pf-c-notification-drawer__group--m-expanded--group--BorderTopWidth:var(--pf-global--BorderWidth--sm);--pf-c-notification-drawer__group--m-expanded--group--BorderTopColor:var(--pf-global--BorderColor--100);--pf-c-notification-drawer__group--m-expanded--MinHeight:0;--pf-c-notification-drawer__group-toggle--PaddingTop:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle--PaddingRight:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle--PaddingBottom:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle--PaddingLeft:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-notification-drawer__group-toggle--BorderColor:var(--pf-global--BorderColor--100);--pf-c-notification-drawer__group-toggle--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-notification-drawer__group-toggle--OutlineOffset:-0.25rem;--pf-c-notification-drawer__group-toggle-title--MarginRight:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle-title--max-lines:1;--pf-c-notification-drawer__group-toggle-count--MarginRight:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle-icon--MarginRight:var(--pf-global--spacer--md);--pf-c-notification-drawer__group-toggle-icon--Color:var(--pf-global--Color--200);--pf-c-notification-drawer__group-toggle-icon--Transition:.2s ease-in 0s;--pf-c-notification-drawer__group--m-expanded__group-toggle-icon--Rotate:90deg;display:flex;flex-direction:column;height:100%;background-color:var(--pf-c-notification-drawer--BackgroundColor)}.pf-c-notification-drawer__header{position:relative;z-index:var(--pf-c-notification-drawer__header--ZIndex);display:flex;flex-shrink:0;align-items:baseline;padding:var(--pf-c-notification-drawer__header--PaddingTop) var(--pf-c-notification-drawer__header--PaddingRight) var(--pf-c-notification-drawer__header--PaddingBottom) var(--pf-c-notification-drawer__header--PaddingLeft);background-color:var(--pf-c-notification-drawer__header--BackgroundColor);box-shadow:var(--pf-c-notification-drawer__header--BoxShadow)}.pf-c-notification-drawer__header-title{font-size:var(--pf-c-notification-drawer__header-title--FontSize)}.pf-c-notification-drawer__header-status{margin-left:var(--pf-c-notification-drawer__header-status--MarginLeft)}.pf-c-notification-drawer__header-action{display:flex;align-items:center;margin-left:auto}.pf-c-notification-drawer__body{overflow-y:auto;box-shadow:var(--pf-c-notification-drawer__body--ZIndex)}.pf-c-notification-drawer__list-item{position:relative;display:grid;grid-template-columns:1fr auto;padding:var(--pf-c-notification-drawer__list-item--PaddingTop) var(--pf-c-notification-drawer__list-item--PaddingRight) var(--pf-c-notification-drawer__list-item--PaddingBottom) var(--pf-c-notification-drawer__list-item--PaddingLeft);background-color:var(--pf-c-notification-drawer__list-item--BackgroundColor);border-bottom:var(--pf-c-notification-drawer__list-item--BorderBottomWidth) solid var(--pf-c-notification-drawer__list-item--BorderBottomColor);outline-offset:var(--pf-c-notification-drawer__list-item--OutlineOffset);box-shadow:var(--pf-c-notification-drawer__list-item--BoxShadow)}.pf-c-notification-drawer__list-item.pf-m-read,.pf-c-notification-drawer__list-item:first-child{--pf-c-notification-drawer__list-item--BoxShadow:none}.pf-c-notification-drawer__list-item:not(.pf-m-read)+.pf-c-notification-drawer__list-item.pf-m-read{--pf-c-notification-drawer__list-item--BoxShadow:var(--pf-c-notification-drawer__list-item--list-item--m-read--BoxShadow);--pf-c-notification-drawer__list-item--before--Top:var(--pf-c-notification-drawer__list-item--list-item--m-read--before--Top)}.pf-c-notification-drawer__list-item:before{position:absolute;top:var(--pf-c-notification-drawer__list-item--before--Top);bottom:var(--pf-c-notification-drawer__list-item--before--Bottom);width:var(--pf-c-notification-drawer__list-item--before--Width);content:"";background-color:var(--pf-c-notification-drawer__list-item--before--BackgroundColor)}.pf-c-notification-drawer__list-item.pf-m-info{--pf-c-notification-drawer__list-item--before--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-info__list-item--before--BackgroundColor);--pf-c-notification-drawer__list-item-header-icon--Color:var(--pf-c-notification-drawer__list-item--m-info__list-item-header-icon--Color)}.pf-c-notification-drawer__list-item.pf-m-warning{--pf-c-notification-drawer__list-item--before--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-warning__list-item--before--BackgroundColor);--pf-c-notification-drawer__list-item-header-icon--Color:var(--pf-c-notification-drawer__list-item--m-warning__list-item-header-icon--Color)}.pf-c-notification-drawer__list-item.pf-m-danger{--pf-c-notification-drawer__list-item--before--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-danger__list-item--before--BackgroundColor);--pf-c-notification-drawer__list-item-header-icon--Color:var(--pf-c-notification-drawer__list-item--m-danger__list-item-header-icon--Color)}.pf-c-notification-drawer__list-item.pf-m-success{--pf-c-notification-drawer__list-item--before--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-success__list-item--before--BackgroundColor);--pf-c-notification-drawer__list-item-header-icon--Color:var(--pf-c-notification-drawer__list-item--m-success__list-item-header-icon--Color)}.pf-c-notification-drawer__list-item.pf-m-default{--pf-c-notification-drawer__list-item--before--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-default__list-item--before--BackgroundColor);--pf-c-notification-drawer__list-item-header-icon--Color:var(--pf-c-notification-drawer__list-item--m-default__list-item-header-icon--Color)}.pf-c-notification-drawer__list-item.pf-m-read{--pf-c-notification-drawer__list-item--BorderBottomColor:var(--pf-c-notification-drawer__list-item--m-read--BorderBottomColor);--pf-c-notification-drawer__list-item--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-read--BackgroundColor);--pf-c-notification-drawer__list-item--before--Top:var(--pf-c-notification-drawer__list-item--m-read--before--Top);--pf-c-notification-drawer__list-item--before--Bottom:var(--pf-c-notification-drawer__list-item--m-read--before--Bottom);--pf-c-notification-drawer__list-item--before--BackgroundColor:var(--pf-c-notification-drawer__list-item--m-read--before--BackgroundColor);--pf-c-notification-drawer__list-item-header-title--FontWeight:var(--pf-c-notification-drawer__list-item--m-read__list-item-header-title--FontWeight);position:relative}.pf-c-notification-drawer__list-item.pf-m-hoverable{cursor:pointer}.pf-c-notification-drawer__list-item.pf-m-hoverable:hover{z-index:var(--pf-c-notification-drawer__list-item--m-hoverable--hover--ZIndex);box-shadow:var(--pf-c-notification-drawer__list-item--m-hoverable--hover--BoxShadow)}.pf-c-notification-drawer__list-item-header{display:flex;align-items:baseline;grid-column:1/2;grid-row:1/2;margin-bottom:var(--pf-c-notification-drawer__list-item-header--MarginBottom)}.pf-c-notification-drawer__list-item-header-icon{margin-right:var(--pf-c-notification-drawer__list-item-header-icon--MarginRight);color:var(--pf-c-notification-drawer__list-item-header-icon--Color)}.pf-c-notification-drawer__list-item-header-title{font-weight:var(--pf-c-notification-drawer__list-item-header-title--FontWeight);word-break:break-word}.pf-c-notification-drawer__list-item-header-title.pf-m-truncate{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--pf-c-notification-drawer__list-item-header-title--max-lines);overflow:hidden}.pf-c-notification-drawer__list-item-action{grid-column:2/3;grid-row:1/3}.pf-c-notification-drawer__list-item-description{grid-row:2/3;grid-column:1/2;margin-bottom:var(--pf-c-notification-drawer__list-item-description--MarginBottom);word-break:break-word}.pf-c-notification-drawer__list-item-timestamp{grid-row:3/4;grid-column:1/2;font-size:var(--pf-c-notification-drawer__list-item-timestamp--FontSize);color:var(--pf-c-notification-drawer__list-item-timestamp--Color)}.pf-c-notification-drawer__group-list{display:flex;flex-direction:column}.pf-c-notification-drawer__group.pf-m-expanded{min-height:var(--pf-c-notification-drawer__group--m-expanded--MinHeight)}.pf-c-notification-drawer__group.pf-m-expanded+.pf-c-notification-drawer__group{border-top:var(--pf-c-notification-drawer__group--m-expanded--group--BorderTopWidth) solid var(--pf-c-notification-drawer__group--m-expanded--group--BorderTopColor)}.pf-c-notification-drawer__group .pf-c-notification-drawer__list-item:last-child{--pf-c-notification-drawer__list-item--BorderBottomWidth:0;--pf-c-notification-drawer__list-item--before--Bottom:0}.pf-c-notification-drawer__group-toggle{display:flex;align-items:baseline;width:100%;padding:var(--pf-c-notification-drawer__group-toggle--PaddingTop) var(--pf-c-notification-drawer__group-toggle--PaddingRight) var(--pf-c-notification-drawer__group-toggle--PaddingBottom) var(--pf-c-notification-drawer__group-toggle--PaddingLeft);background-color:var(--pf-c-notification-drawer__group-toggle--BackgroundColor);border:solid var(--pf-c-notification-drawer__group-toggle--BorderColor);border-left-width:0;border-bottom-width:var(--pf-c-notification-drawer__group-toggle--BorderBottomWidth);border-right-width:0;border-top-width:0;outline-offset:var(--pf-c-notification-drawer__group-toggle--OutlineOffset)}.pf-c-notification-drawer__group-toggle-title{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--pf-c-notification-drawer__group-toggle-title--max-lines);overflow:hidden;margin-right:var(--pf-c-notification-drawer__group-toggle-title--MarginRight);text-align:left;word-break:break-word}.pf-c-notification-drawer__group-toggle-count{margin-right:var(--pf-c-notification-drawer__group-toggle-count--MarginRight);margin-left:auto}.pf-c-notification-drawer__group-toggle-icon{margin-right:var(--pf-c-notification-drawer__group-toggle-icon--MarginRight);color:var(--pf-c-notification-drawer__group-toggle-icon--Color);transition:var(--pf-c-notification-drawer__group-toggle-icon--Transition)}.pf-c-notification-drawer__group.pf-m-expanded .pf-c-notification-drawer__group-toggle-icon{transform:rotate(var(--pf-c-notification-drawer__group--m-expanded__group-toggle-icon--Rotate))}.pf-c-options-menu{--pf-c-options-menu__toggle--BackgroundColor:transparent;--pf-c-options-menu__toggle--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-options-menu__toggle--PaddingRight:var(--pf-global--spacer--sm);--pf-c-options-menu__toggle--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-options-menu__toggle--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-options-menu__toggle--MinWidth:var(--pf-global--target-size--MinWidth);--pf-c-options-menu__toggle--LineHeight:var(--pf-global--LineHeight--md);--pf-c-options-menu__toggle--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-options-menu__toggle--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-options-menu__toggle--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-options-menu__toggle--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-options-menu__toggle--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-options-menu__toggle--Color:var(--pf-global--Color--100);--pf-c-options-menu__toggle--hover--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-options-menu__toggle--active--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-options-menu__toggle--active--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-options-menu__toggle--focus--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-options-menu__toggle--focus--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-options-menu__toggle--expanded--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-options-menu__toggle--expanded--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-options-menu__toggle--disabled--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-options-menu__toggle--m-plain--Color:var(--pf-global--Color--200);--pf-c-options-menu__toggle--m-plain--hover--Color:var(--pf-global--Color--100);--pf-c-options-menu__toggle--m-plain--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-options-menu__toggle-icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-options-menu__toggle-icon--MarginLeft:var(--pf-global--spacer--md);--pf-c-options-menu--m-top--m-expanded__toggle-icon--Rotate:180deg;--pf-c-options-menu__toggle-button--BackgroundColor:transparent;--pf-c-options-menu__toggle-button--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-options-menu__toggle-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-options-menu__toggle-button--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-options-menu__toggle-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-options-menu__menu--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-options-menu__menu--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-options-menu__menu--PaddingTop:var(--pf-global--spacer--sm);--pf-c-options-menu__menu--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-options-menu__menu--Top:calc(100% + var(--pf-global--spacer--xs));--pf-c-options-menu__menu--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-options-menu--m-top__menu--Top:0;--pf-c-options-menu--m-top__menu--TranslateY:calc(-100% - var(--pf-global--spacer--xs));--pf-c-options-menu__menu-item--BackgroundColor:transparent;--pf-c-options-menu__menu-item--Color:var(--pf-global--Color--100);--pf-c-options-menu__menu-item--FontSize:var(--pf-global--FontSize--md);--pf-c-options-menu__menu-item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-options-menu__menu-item--PaddingRight:var(--pf-global--spacer--md);--pf-c-options-menu__menu-item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-options-menu__menu-item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-options-menu__menu-item--disabled--Color:var(--pf-global--Color--dark-200);--pf-c-options-menu__menu-item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-options-menu__menu-item--disabled--BackgroundColor:transparent;--pf-c-options-menu__menu-item-icon--Color:var(--pf-global--active-color--100);--pf-c-options-menu__menu-item-icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-options-menu__menu-item-icon--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-options-menu__group--group--PaddingTop:var(--pf-global--spacer--sm);--pf-c-options-menu__group-title--PaddingTop:var(--pf-global--spacer--sm);--pf-c-options-menu__group-title--PaddingRight:var(--pf-c-options-menu__menu-item--PaddingRight);--pf-c-options-menu__group-title--PaddingBottom:var(--pf-c-options-menu__menu-item--PaddingBottom);--pf-c-options-menu__group-title--PaddingLeft:var(--pf-c-options-menu__menu-item--PaddingLeft);--pf-c-options-menu__group-title--FontSize:var(--pf-global--FontSize--sm);--pf-c-options-menu__group-title--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-options-menu__group-title--Color:var(--pf-global--Color--dark-200);--pf-c-options-menu--c-divider--MarginTop:var(--pf-global--spacer--sm);--pf-c-options-menu--c-divider--MarginBottom:var(--pf-global--spacer--sm);position:relative;display:inline-block;max-width:100%}.pf-c-options-menu .pf-c-divider{margin-top:var(--pf-c-options-menu--c-divider--MarginTop);margin-bottom:var(--pf-c-options-menu--c-divider--MarginBottom)}.pf-c-options-menu .pf-c-divider:last-child{--pf-c-options-menu--c-divider--MarginBottom:0}.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:before,.pf-c-options-menu__toggle:not(.pf-m-plain):before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-options-menu__toggle--BorderWidth) solid;border-color:var(--pf-c-options-menu__toggle--BorderTopColor) var(--pf-c-options-menu__toggle--BorderRightColor) var(--pf-c-options-menu__toggle--BorderBottomColor) var(--pf-c-options-menu__toggle--BorderLeftColor)}.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:hover:before,.pf-c-options-menu__toggle:not(.pf-m-plain):hover:before{--pf-c-options-menu__toggle--BorderBottomColor:var(--pf-c-options-menu__toggle--hover--BorderBottomColor)}.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button.pf-m-active:before,.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:active:before,.pf-c-options-menu__toggle:not(.pf-m-plain).pf-m-active:before,.pf-c-options-menu__toggle:not(.pf-m-plain):active:before{--pf-c-options-menu__toggle--BorderBottomColor:var(--pf-c-options-menu__toggle--active--BorderBottomColor);border-bottom-width:var(--pf-c-options-menu__toggle--active--BorderBottomWidth)}.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:focus:before,.pf-c-options-menu__toggle:not(.pf-m-plain):focus:before{--pf-c-options-menu__toggle--BorderBottomColor:var(--pf-c-options-menu__toggle--focus--BorderBottomColor);border-bottom-width:var(--pf-c-options-menu__toggle--focus--BorderBottomWidth)}.pf-c-options-menu__toggle{position:relative;display:flex;align-items:center;justify-content:space-between;min-width:var(--pf-c-options-menu__toggle--MinWidth);max-width:100%;padding-left:var(--pf-c-options-menu__toggle--PaddingLeft);line-height:var(--pf-c-options-menu__toggle--LineHeight);color:var(--pf-c-options-menu__toggle--Color);background-color:var(--pf-c-options-menu__toggle--BackgroundColor);border:none}.pf-c-options-menu__toggle:not(.pf-m-text){padding-top:var(--pf-c-options-menu__toggle--PaddingTop);padding-right:var(--pf-c-options-menu__toggle--PaddingRight);padding-bottom:var(--pf-c-options-menu__toggle--PaddingBottom)}.pf-c-options-menu.pf-m-expanded>.pf-c-options-menu__toggle:before{--pf-c-options-menu__toggle--BorderBottomColor:var(--pf-c-options-menu__toggle--expanded--BorderBottomColor);border-bottom-width:var(--pf-c-options-menu__toggle--expanded--BorderBottomWidth)}.pf-c-options-menu__toggle.pf-m-plain:not(.pf-m-text){justify-content:center;color:var(--pf-c-options-menu__toggle--m-plain--Color)}.pf-c-options-menu__toggle.pf-m-plain .pf-c-options-menu__toggle-button-icon{line-height:var(--pf-c-options-menu__toggle--LineHeight)}.pf-c-options-menu.pf-m-expanded>.pf-c-options-menu__toggle.pf-m-plain,.pf-c-options-menu__toggle.pf-m-plain.pf-m-active,.pf-c-options-menu__toggle.pf-m-plain:active,.pf-c-options-menu__toggle.pf-m-plain:focus,.pf-c-options-menu__toggle.pf-m-plain:hover{--pf-c-options-menu__toggle--m-plain--Color:var(--pf-c-options-menu__toggle--m-plain--hover--Color)}.pf-c-options-menu__toggle.pf-m-plain.pf-m-disabled,.pf-c-options-menu__toggle.pf-m-plain:disabled{--pf-c-options-menu__toggle--m-plain--Color:var(--pf-c-options-menu__toggle--m-plain--disabled--Color)}.pf-c-options-menu__toggle.pf-m-disabled,.pf-c-options-menu__toggle:disabled{pointer-events:none}.pf-c-options-menu__toggle.pf-m-disabled.pf-m-text,.pf-c-options-menu__toggle.pf-m-disabled:not(.pf-m-plain),.pf-c-options-menu__toggle:disabled.pf-m-text,.pf-c-options-menu__toggle:disabled:not(.pf-m-plain){--pf-c-options-menu__toggle--BackgroundColor:var(--pf-c-options-menu__toggle--disabled--BackgroundColor)}.pf-c-options-menu__toggle.pf-m-disabled:before,.pf-c-options-menu__toggle:disabled:before{border:0}.pf-c-options-menu__toggle-button-icon{position:relative}.pf-c-options-menu__toggle-button{padding:var(--pf-c-options-menu__toggle-button--PaddingTop) var(--pf-c-options-menu__toggle-button--PaddingRight) var(--pf-c-options-menu__toggle-button--PaddingBottom) var(--pf-c-options-menu__toggle-button--PaddingLeft);background-color:var(--pf-c-options-menu__toggle-button--BackgroundColor);border:0}.pf-c-options-menu__toggle-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-c-options-menu__toggle-icon{margin-right:var(--pf-c-options-menu__toggle-icon--MarginRight);margin-left:var(--pf-c-options-menu__toggle-icon--MarginLeft)}.pf-c-options-menu.pf-m-top.pf-m-expanded .pf-c-options-menu__toggle-icon{transform:rotate(var(--pf-c-options-menu--m-top--m-expanded__toggle-icon--Rotate))}.pf-c-options-menu__menu{position:absolute;top:var(--pf-c-options-menu__menu--Top);z-index:var(--pf-c-options-menu__menu--ZIndex);min-width:100%;padding-top:var(--pf-c-options-menu__menu--PaddingTop);padding-bottom:var(--pf-c-options-menu__menu--PaddingBottom);background-color:var(--pf-c-options-menu__menu--BackgroundColor);background-clip:padding-box;box-shadow:var(--pf-c-options-menu__menu--BoxShadow)}.pf-c-options-menu__menu.pf-m-align-right{right:0}.pf-c-options-menu.pf-m-top .pf-c-options-menu__menu{--pf-c-options-menu__menu--Top:var(--pf-c-options-menu--m-top__menu--Top);transform:translateY(var(--pf-c-options-menu--m-top__menu--TranslateY))}.pf-c-options-menu__menu-item{display:flex;align-items:baseline;width:100%;padding:var(--pf-c-options-menu__menu-item--PaddingTop) var(--pf-c-options-menu__menu-item--PaddingRight) var(--pf-c-options-menu__menu-item--PaddingBottom) var(--pf-c-options-menu__menu-item--PaddingLeft);font-size:var(--pf-c-options-menu__menu-item--FontSize);color:var(--pf-c-options-menu__menu-item--Color);white-space:nowrap;background-color:var(--pf-c-options-menu__menu-item--BackgroundColor);border:none}.pf-c-options-menu__menu-item:focus,.pf-c-options-menu__menu-item:hover{text-decoration:none;background-color:var(--pf-c-options-menu__menu-item--hover--BackgroundColor)}.pf-c-options-menu__menu-item.pf-m-disabled,.pf-c-options-menu__menu-item:disabled{color:var(--pf-c-options-menu__menu-item--disabled--Color);pointer-events:none;background-color:var(--pf-c-options-menu__menu-item--disabled--BackgroundColor)}.pf-c-options-menu__menu-item-icon{align-self:center;width:auto;padding-left:var(--pf-c-options-menu__menu-item-icon--PaddingLeft);margin-left:auto;font-size:var(--pf-c-options-menu__menu-item-icon--FontSize);color:var(--pf-c-options-menu__menu-item-icon--Color)}.pf-c-options-menu__group+.pf-c-options-menu__group{padding-top:var(--pf-c-options-menu__group--group--PaddingTop)}.pf-c-options-menu__group-title{padding:var(--pf-c-options-menu__group-title--PaddingTop) var(--pf-c-options-menu__group-title--PaddingRight) var(--pf-c-options-menu__group-title--PaddingBottom) var(--pf-c-options-menu__group-title--PaddingLeft);font-size:var(--pf-c-options-menu__group-title--FontSize);font-weight:var(--pf-c-options-menu__group-title--FontWeight);color:var(--pf-c-options-menu__group-title--Color)}.pf-c-overflow-menu{--pf-c-overflow-menu--spacer--base:var(--pf-global--spacer--md);--pf-c-overflow-menu--spacer:var(--pf-global--spacer--sm);--pf-c-overflow-menu__group--spacer:var(--pf-c-overflow-menu--spacer--base);--pf-c-overflow-menu__item--spacer:var(--pf-c-overflow-menu--spacer--base);--pf-c-overflow-menu--c-divider--m-vertical--spacer:var(--pf-c-overflow-menu--spacer--base);--pf-c-overflow-menu__group--m-button-group--spacer:var(--pf-c-overflow-menu--spacer--base);--pf-c-overflow-menu__group--m-button-group--space-items:var(--pf-global--spacer--sm);--pf-c-overflow-menu__group--m-icon-button-group--spacer:var(--pf-c-overflow-menu--spacer--base);--pf-c-overflow-menu__group--m-icon-button-group--space-items:0;display:inline-flex;align-items:center}.pf-c-overflow-menu__content,.pf-c-overflow-menu__group{display:flex;align-items:center}.pf-c-overflow-menu__group{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu__group--spacer)}.pf-c-overflow-menu__group.pf-m-button-group{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu__group--m-button-group--spacer)}.pf-c-overflow-menu__group.pf-m-button-group>*{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu__group--m-button-group--space-items)}.pf-c-overflow-menu__group.pf-m-icon-button-group{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu__group--m-icon-button-group--spacer)}.pf-c-overflow-menu__group.pf-m-icon-button-group>*{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu__group--m-icon-button-group--space-items)}.pf-c-overflow-menu__item{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu__item--spacer)}.pf-c-overflow-menu__content,.pf-c-overflow-menu__control,.pf-c-overflow-menu__group,.pf-c-overflow-menu__item{margin-right:var(--pf-c-overflow-menu--spacer)}.pf-c-overflow-menu__content:last-child,.pf-c-overflow-menu__control:last-child,.pf-c-overflow-menu__group:last-child,.pf-c-overflow-menu__item:last-child{--pf-c-overflow-menu--spacer:0}.pf-c-overflow-menu>.pf-c-divider,.pf-c-overflow-menu__group>.pf-c-divider{--pf-c-overflow-menu--spacer:var(--pf-c-overflow-menu--c-divider--m-vertical--spacer)}.pf-c-overflow-menu>.pf-c-divider.pf-m-vertical,.pf-c-overflow-menu__group>.pf-c-divider.pf-m-vertical{margin-right:var(--pf-c-overflow-menu--spacer)}.pf-c-overflow-menu>.pf-c-divider.pf-m-vertical:last-child,.pf-c-overflow-menu__group>.pf-c-divider.pf-m-vertical:last-child{--pf-c-overflow-menu--spacer:0}.pf-c-page{--pf-c-page--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-page__header--BackgroundColor:var(--pf-global--BackgroundColor--dark-100);--pf-c-page__header--ZIndex:var(--pf-global--ZIndex--md);--pf-c-page__header--MinHeight:4.75rem;--pf-c-page__header-brand--PaddingLeft:var(--pf-global--spacer--md);--pf-c-page__header-brand--xl--PaddingRight:var(--pf-global--spacer--xl);--pf-c-page__header-brand--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-page__header-sidebar-toggle__c-button--PaddingTop:var(--pf-global--spacer--sm);--pf-c-page__header-sidebar-toggle__c-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-page__header-sidebar-toggle__c-button--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-page__header-sidebar-toggle__c-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-page__header-sidebar-toggle__c-button--MarginRight:var(--pf-global--spacer--md);--pf-c-page__header-sidebar-toggle__c-button--MarginLeft:calc(var(--pf-c-page__header-sidebar-toggle__c-button--PaddingLeft)*-1);--pf-c-page__header-sidebar-toggle__c-button--FontSize:var(--pf-global--FontSize--2xl);--pf-c-page__header-brand-link--c-brand--MaxHeight:3.75rem;--pf-c-page__header-nav--BackgroundColor:var(--pf-global--BackgroundColor--dark-300);--pf-c-page__header-nav--xl--BackgroundColor:transparent;--pf-c-page__header-nav--xl--PaddingRight:var(--pf-global--spacer--xl);--pf-c-page__header-nav--xl--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-page__header-tools--MarginRight:var(--pf-global--spacer--md);--pf-c-page__header-tools--xl--MarginRight:var(--pf-global--spacer--lg);--pf-c-page__header-tools--c-avatar--MarginLeft:var(--pf-global--spacer--md);--pf-c-page__header-tools-group--MarginLeft:var(--pf-global--spacer--xl);--pf-c-page__header-tools-group--Display:flex;--pf-c-page__header-tools-item--Display:block;--pf-c-page__header-tools-item--c-notification-badge--hover--BackgroundColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-page__header-tools--c-button--notification-badge--m-unread--after--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-page__header-tools--c-button--notification-badge--m-attention--after--BackgroundColor:var(--pf-global--danger-color--200);--pf-c-page__header-tools--c-button--m-selected--notification-badge--m-unread--after--BackgroundColor:var(--pf-global--primary-color--200);--pf-c-page__header-tools--c-button--m-selected--notification-badge--m-attention--after--BackgroundColor:var(--pf-global--danger-color--200);--pf-c-page__header-tools--c-button--m-selected--before--Width:auto;--pf-c-page__header-tools--c-button--m-selected--before--Height:auto;--pf-c-page__header-tools--c-button--m-selected--before--BackgroundColor:var(--pf-global--BackgroundColor--dark-200);--pf-c-page__header-tools--c-button--m-selected--before--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-page__header-tools--c-button--m-selected--c-notification-badge--m-unread--after--BorderColor:transparent;--pf-c-page__sidebar--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-page__sidebar--Width:80%;--pf-c-page__sidebar--Width:18.125rem;--pf-c-page__sidebar--BackgroundColor:var(--pf-global--BackgroundColor--dark-300);--pf-c-page__sidebar--m-light--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-page__sidebar--BoxShadow:var(--pf-global--BoxShadow--lg-right);--pf-c-page__sidebar--Transition:var(--pf-global--Transition);--pf-c-page__sidebar--TranslateX:-100%;--pf-c-page__sidebar--TranslateZ:0;--pf-c-page__sidebar--m-expanded--TranslateX:0;--pf-c-page__sidebar--xl--TranslateX:0;--pf-c-page__sidebar-body--PaddingTop:var(--pf-global--spacer--sm);--pf-c-page__sidebar-body--PaddingBottom:var(--pf-global--spacer--md);--pf-c-page__main--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-page__main-section--PaddingTop:var(--pf-global--spacer--md);--pf-c-page__main-section--PaddingRight:var(--pf-global--spacer--md);--pf-c-page__main-section--PaddingBottom:var(--pf-global--spacer--md);--pf-c-page__main-section--PaddingLeft:var(--pf-global--spacer--md);--pf-c-page__main-section--xl--PaddingTop:var(--pf-global--spacer--lg);--pf-c-page__main-section--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-page__main-section--xl--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-page__main-section--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-page__main-breadcrumb--main-section--PaddingTop:var(--pf-global--spacer--md);--pf-c-page__main-section--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-page__main-section--m-light--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-page__main-section--m-dark-100--BackgroundColor:var(--pf-global--BackgroundColor--dark-transparent-100);--pf-c-page__main-section--m-dark-200--BackgroundColor:var(--pf-global--BackgroundColor--dark-transparent-200);--pf-c-page--section--m-limit-width--MaxWidth:calc(125rem - var(--pf-c-page__sidebar--Width));--pf-c-page--section--m-sticky-top--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-page--section--m-sticky-top--BoxShadow:var(--pf-global--BoxShadow--sm-bottom);--pf-c-page--section--m-sticky-bottom--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-page--section--m-sticky-bottom--BoxShadow:var(--pf-global--BoxShadow--sm-top);--pf-c-page--section--m-shadow-bottom--BoxShadow:var(--pf-global--BoxShadow--sm-bottom);--pf-c-page--section--m-shadow-bottom--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-page--section--m-shadow-top--BoxShadow:var(--pf-global--BoxShadow--sm-top);--pf-c-page--section--m-shadow-top--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-page__main-nav--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-page__main-nav--PaddingTop:var(--pf-global--spacer--md);--pf-c-page__main-nav--PaddingRight:0;--pf-c-page__main-nav--PaddingLeft:0;--pf-c-page__main-nav--m-sticky-top--PaddingBottom:var(--pf-global--spacer--md);--pf-c-page__main-nav--xl--PaddingRight:var(--pf-global--spacer--sm);--pf-c-page__main-nav--xl--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-page__main-breadcrumb--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-page__main-breadcrumb--PaddingTop:var(--pf-global--spacer--md);--pf-c-page__main-breadcrumb--PaddingRight:var(--pf-global--spacer--md);--pf-c-page__main-breadcrumb--PaddingBottom:0;--pf-c-page__main-breadcrumb--PaddingLeft:var(--pf-global--spacer--md);--pf-c-page__main-breadcrumb--m-sticky-top--PaddingBottom:var(--pf-global--spacer--md);--pf-c-page__main-breadcrumb--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-page__main-breadcrumb--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-page__main-wizard--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-page__main-wizard--BorderTopColor:var(--pf-global--BorderColor--100);--pf-c-page__main-wizard--BorderTopWidth:var(--pf-global--BorderWidth--sm);display:grid;height:100%;grid-template-columns:1fr;grid-template-rows:max-content 1fr;grid-template-areas:"header" "main";background-color:var(--pf-c-page--BackgroundColor)}@media (min-width:1200px){.pf-c-page{--pf-c-page__header-brand--PaddingLeft:var(--pf-c-page__header-brand--xl--PaddingLeft)}}@media screen and (min-width:1200px){.pf-c-page{--pf-c-page__header-nav--BackgroundColor:var(--pf-c-page__header-nav--xl--BackgroundColor);--pf-c-page__header-nav--PaddingRight:var(--pf-c-page__header-nav--xl--PaddingRight);--pf-c-page__header-nav--PaddingLeft:var(--pf-c-page__header-nav--xl--PaddingLeft);--pf-c-page__header-tools--MarginRight:var(--pf-c-page__header-tools--xl--MarginRight);--pf-c-page__sidebar--TranslateX:var(--pf-c-page__sidebar--xl--TranslateX);--pf-c-page__main-section--PaddingTop:var(--pf-c-page__main-section--xl--PaddingTop);--pf-c-page__main-section--PaddingRight:var(--pf-c-page__main-section--xl--PaddingRight);--pf-c-page__main-section--PaddingBottom:var(--pf-c-page__main-section--xl--PaddingBottom);--pf-c-page__main-section--PaddingLeft:var(--pf-c-page__main-section--xl--PaddingLeft);--pf-c-page__main-nav--PaddingRight:var(--pf-c-page__main-nav--xl--PaddingRight);--pf-c-page__main-nav--PaddingLeft:var(--pf-c-page__main-nav--xl--PaddingLeft);--pf-c-page__main-breadcrumb--PaddingRight:var(--pf-c-page__main-breadcrumb--xl--PaddingRight);--pf-c-page__main-breadcrumb--PaddingLeft:var(--pf-c-page__main-breadcrumb--xl--PaddingLeft)}}@media (min-width:1200px){.pf-c-page{grid-template-columns:max-content 1fr;grid-template-areas:"header header" "nav main"}}.pf-c-page__header{color:var(--pf-global--Color--100);z-index:var(--pf-c-page__header--ZIndex);grid-template-columns:auto auto;display:grid;grid-area:header;align-items:center;min-width:0;min-height:var(--pf-c-page__header--MinHeight);background-color:var(--pf-c-page__header--BackgroundColor)}.pf-c-page__header>*{display:flex;align-items:center}@media screen and (min-width:992px){.pf-c-page__header{grid-template-columns:auto 1fr auto}}.pf-c-page__header-brand{grid-column:1/2;padding-left:var(--pf-c-page__header-brand--PaddingLeft)}@media (min-width:1200px){.pf-c-page__header-brand{padding-right:var(--pf-c-page__header-brand--xl--PaddingRight)}}.pf-c-page__header-brand-link{display:flex;flex:1;align-items:center}.pf-c-page__header-brand-link .pf-c-brand{max-height:var(--pf-c-page__header-brand-link--c-brand--MaxHeight)}.pf-c-page__header-brand-toggle .pf-c-button{padding:var(--pf-c-page__header-sidebar-toggle__c-button--PaddingTop) var(--pf-c-page__header-sidebar-toggle__c-button--PaddingRight) var(--pf-c-page__header-sidebar-toggle__c-button--PaddingBottom) var(--pf-c-page__header-sidebar-toggle__c-button--PaddingLeft);margin-right:var(--pf-c-page__header-sidebar-toggle__c-button--MarginRight);margin-left:var(--pf-c-page__header-sidebar-toggle__c-button--MarginLeft);font-size:var(--pf-c-page__header-sidebar-toggle__c-button--FontSize);line-height:1}.pf-c-page__header-nav{align-self:stretch;min-width:0;padding-right:var(--pf-c-page__header-nav--PaddingRight);padding-left:var(--pf-c-page__header-nav--PaddingLeft);background-color:var(--pf-c-page__header-nav--BackgroundColor);grid-column:1/-1;grid-row:2/3}@media screen and (min-width:1200px){.pf-c-page__header-nav{grid-column:2/3;grid-row:1/2}}.pf-c-page__header-nav .pf-c-nav{align-self:stretch}.pf-c-page__header-tools{grid-column:2/3;margin-right:var(--pf-c-page__header-tools--MarginRight);margin-left:auto}.pf-c-page__header-tools .pf-c-avatar{margin-left:var(--pf-c-page__header-tools--c-avatar--MarginLeft)}@media screen and (min-width:992px){.pf-c-page__header-tools{grid-column:3/4}}.pf-c-page__header-tools-group{--pf-hidden-visible--visible--Display:var(--pf-c-page__header-tools-group--Display);align-items:center}.pf-c-page__header-tools-group+.pf-c-page__header-tools-group{margin-left:var(--pf-c-page__header-tools-group--MarginLeft)}.pf-c-page__header-tools-item{--pf-hidden-visible--visible--Display:var(--pf-c-page__header-tools-item--Display)}.pf-c-page__header-tools-item .pf-c-notification-badge.pf-m-read:hover{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-page__header-tools-item--c-notification-badge--hover--BackgroundColor)}.pf-c-page__header-tools-item.pf-m-selected .pf-c-button{background-color:var(--pf-c-page__header-tools--c-button--m-selected--before--BackgroundColor);border-radius:var(--pf-c-page__header-tools--c-button--m-selected--before--BorderRadius)}.pf-c-page__header-tools-item.pf-m-selected .pf-c-button:before{position:absolute;top:0;right:0;bottom:0;left:0;width:var(--pf-c-page__header-tools--c-button--m-selected--before--Width);height:var(--pf-c-page__header-tools--c-button--m-selected--before--Height);content:""}.pf-c-page__header-tools-item.pf-m-selected .pf-c-button .pf-c-notification-badge.pf-m-unread{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-page__header-tools--c-button--m-selected--notification-badge--m-unread--after--BackgroundColor)}.pf-c-page__header-tools-item.pf-m-selected .pf-c-button .pf-c-notification-badge.pf-m-unread:after{border-color:var(--pf-c-page__header-tools--c-button--m-selected--c-notification-badge--m-unread--after--BorderColor)}.pf-c-page__header-tools-item.pf-m-selected .pf-c-button .pf-c-notification-badge.pf-m-attention{--pf-c-notification-badge--after--BackgroundColor:var(--pf-global--danger-color--200)}.pf-c-page__header-tools-item .pf-c-button:focus .pf-c-notification-badge.pf-m-unread{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-page__header-tools--c-button--notification-badge--m-unread--after--BackgroundColor)}.pf-c-page__header-tools-item .pf-c-button:focus .pf-c-notification-badge.pf-m-attention{--pf-c-notification-badge--after--BackgroundColor:var(--pf-c-page__header-tools--c-button--notification-badge--m-attention--after--BackgroundColor)}.pf-c-page__sidebar{grid-area:nav;grid-row-start:2;grid-column-start:1;z-index:var(--pf-c-page__sidebar--ZIndex);width:var(--pf-c-page__sidebar--Width);overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch;background-color:var(--pf-c-page__sidebar--BackgroundColor);transition:var(--pf-c-page__sidebar--Transition);transform:translateX(var(--pf-c-page__sidebar--TranslateX)) translateZ(var(--pf-c-page__sidebar--TranslateZ))}@media screen and (min-width:1200px){.pf-c-page__sidebar{box-shadow:var(--pf-c-page__sidebar--BoxShadow)}}.pf-c-page__sidebar.pf-m-expanded{--pf-c-page__sidebar--TranslateX:var(--pf-c-page__sidebar--m-expanded--TranslateX);box-shadow:var(--pf-c-page__sidebar--BoxShadow)}.pf-c-page__sidebar.pf-m-collapsed{max-width:0;overflow:hidden}.pf-c-page__sidebar.pf-m-light{color:var(--pf-global--Color--100);--pf-c-page__sidebar--BackgroundColor:var(--pf-c-page__sidebar--m-light--BackgroundColor)}.pf-c-page__sidebar-body{padding-top:var(--pf-c-page__sidebar-body--PaddingTop);padding-bottom:var(--pf-c-page__sidebar-body--PaddingBottom)}.pf-c-page__main-breadcrumb.pf-m-limit-width,.pf-c-page__main-nav.pf-m-limit-width,.pf-c-page__main-section.pf-m-limit-width,.pf-c-page__main-wizard.pf-m-limit-width{display:flex;flex-direction:column;padding:0}.pf-c-page__main-breadcrumb.pf-m-limit-width>.pf-c-page__main-body,.pf-c-page__main-nav.pf-m-limit-width>.pf-c-page__main-body,.pf-c-page__main-section.pf-m-limit-width>.pf-c-page__main-body,.pf-c-page__main-wizard.pf-m-limit-width>.pf-c-page__main-body{flex:1;max-width:var(--pf-c-page--section--m-limit-width--MaxWidth)}.pf-c-page__main-breadcrumb,.pf-c-page__main-group,.pf-c-page__main-nav,.pf-c-page__main-section,.pf-c-page__main-wizard{flex-shrink:0}.pf-c-page__main-breadcrumb.pf-m-sticky-top,.pf-c-page__main-group.pf-m-sticky-top,.pf-c-page__main-nav.pf-m-sticky-top,.pf-c-page__main-section.pf-m-sticky-top,.pf-c-page__main-wizard.pf-m-sticky-top{position:sticky;top:0;z-index:var(--pf-c-page--section--m-sticky-top--ZIndex);box-shadow:var(--pf-c-page--section--m-sticky-top--BoxShadow)}.pf-c-page__main-breadcrumb.pf-m-sticky-bottom,.pf-c-page__main-group.pf-m-sticky-bottom,.pf-c-page__main-nav.pf-m-sticky-bottom,.pf-c-page__main-section.pf-m-sticky-bottom,.pf-c-page__main-wizard.pf-m-sticky-bottom{position:sticky;bottom:0;z-index:var(--pf-c-page--section--m-sticky-bottom--ZIndex);box-shadow:var(--pf-c-page--section--m-sticky-bottom--BoxShadow)}.pf-c-page__main-breadcrumb.pf-m-overflow-scroll,.pf-c-page__main-group.pf-m-overflow-scroll,.pf-c-page__main-nav.pf-m-overflow-scroll,.pf-c-page__main-section.pf-m-overflow-scroll,.pf-c-page__main-wizard.pf-m-overflow-scroll{position:relative;flex-shrink:1;overflow:auto}.pf-c-page__main-breadcrumb.pf-m-shadow-bottom,.pf-c-page__main-group.pf-m-shadow-bottom,.pf-c-page__main-nav.pf-m-shadow-bottom,.pf-c-page__main-section.pf-m-shadow-bottom,.pf-c-page__main-wizard.pf-m-shadow-bottom{z-index:var(--pf-c-page--section--m-shadow-bottom--ZIndex);box-shadow:var(--pf-c-page--section--m-shadow-bottom--BoxShadow)}.pf-c-page__main-breadcrumb.pf-m-shadow-top,.pf-c-page__main-group.pf-m-shadow-top,.pf-c-page__main-nav.pf-m-shadow-top,.pf-c-page__main-section.pf-m-shadow-top,.pf-c-page__main-wizard.pf-m-shadow-top{z-index:var(--pf-c-page--section--m-shadow-top--ZIndex);box-shadow:var(--pf-c-page--section--m-shadow-top--BoxShadow)}.pf-c-page__drawer,.pf-c-page__main{grid-area:main;z-index:var(--pf-c-page__main--ZIndex);overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch}.pf-c-page__drawer:focus,.pf-c-page__main:focus{outline:0}.pf-c-page__main,.pf-c-page__main-drawer,.pf-c-page__main-group{display:flex;flex-direction:column}.pf-c-page__main-nav{padding-top:var(--pf-c-page__main-nav--PaddingTop);padding-right:var(--pf-c-page__main-nav--PaddingRight);padding-left:var(--pf-c-page__main-nav--PaddingLeft);background-color:var(--pf-c-page__main-nav--BackgroundColor)}.pf-c-page__main-group.pf-m-sticky-top .pf-c-page__main-nav:last-child,.pf-c-page__main-nav.pf-m-sticky-top{padding-bottom:var(--pf-c-page__main-nav--m-sticky-top--PaddingBottom)}.pf-c-page__main-breadcrumb{padding:var(--pf-c-page__main-breadcrumb--PaddingTop) var(--pf-c-page__main-breadcrumb--PaddingRight) var(--pf-c-page__main-breadcrumb--PaddingBottom) var(--pf-c-page__main-breadcrumb--PaddingLeft);background-color:var(--pf-c-page__main-breadcrumb--BackgroundColor)}.pf-c-page__main-breadcrumb+.pf-c-page__main-section{--pf-c-page__main-section--PaddingTop:var(--pf-c-page__main-breadcrumb--main-section--PaddingTop)}.pf-c-page__main-breadcrumb.pf-m-sticky-top,.pf-c-page__main-group.pf-m-sticky-top .pf-c-page__main-breadcrumb:last-child{--pf-c-page__main-breadcrumb--PaddingBottom:var(--pf-c-page__main-breadcrumb--m-sticky-top--PaddingBottom)}.pf-c-page__main-group.pf-m-fill,.pf-c-page__main-group:last-child,.pf-c-page__main-group:only-child,.pf-c-page__main-section.pf-m-fill,.pf-c-page__main-section:last-child,.pf-c-page__main-section:only-child,.pf-c-page__main-wizard.pf-m-fill,.pf-c-page__main-wizard:last-child,.pf-c-page__main-wizard:only-child{flex-grow:1}.pf-c-page__main-group.pf-m-no-fill,.pf-c-page__main-section.pf-m-no-fill,.pf-c-page__main-wizard.pf-m-no-fill{flex-grow:0}.pf-c-page__main-section{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft);background-color:var(--pf-c-page__main-section--BackgroundColor)}.pf-c-page__main-section.pf-m-light{--pf-c-page__main-section--BackgroundColor:var(--pf-c-page__main-section--m-light--BackgroundColor)}.pf-c-page__main-section[class*=pf-m-dark-]{color:var(--pf-global--Color--100)}.pf-c-page__main-section.pf-m-dark-100{--pf-c-page__main-section--BackgroundColor:var(--pf-c-page__main-section--m-dark-100--BackgroundColor)}.pf-c-page__main-section.pf-m-dark-200{--pf-c-page__main-section--BackgroundColor:var(--pf-c-page__main-section--m-dark-200--BackgroundColor)}.pf-c-page__main-section.pf-m-padding{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__main-section.pf-m-no-padding{--pf-c-page__main-section--PaddingTop:0;--pf-c-page__main-section--PaddingRight:0;--pf-c-page__main-section--PaddingBottom:0;--pf-c-page__main-section--PaddingLeft:0}@media (min-width:576px){.pf-c-page__main-section.pf-m-padding-on-sm{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__main-section.pf-m-no-padding-on-sm{--pf-c-page__main-section--PaddingTop:0;--pf-c-page__main-section--PaddingRight:0;--pf-c-page__main-section--PaddingBottom:0;--pf-c-page__main-section--PaddingLeft:0}}@media (min-width:768px){.pf-c-page__main-section.pf-m-padding-on-md{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__main-section.pf-m-no-padding-on-md{--pf-c-page__main-section--PaddingTop:0;--pf-c-page__main-section--PaddingRight:0;--pf-c-page__main-section--PaddingBottom:0;--pf-c-page__main-section--PaddingLeft:0}}@media (min-width:992px){.pf-c-page__main-section.pf-m-padding-on-lg{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__main-section.pf-m-no-padding-on-lg{--pf-c-page__main-section--PaddingTop:0;--pf-c-page__main-section--PaddingRight:0;--pf-c-page__main-section--PaddingBottom:0;--pf-c-page__main-section--PaddingLeft:0}}@media (min-width:1200px){.pf-c-page__main-section.pf-m-padding-on-xl{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__main-section.pf-m-no-padding-on-xl{--pf-c-page__main-section--PaddingTop:0;--pf-c-page__main-section--PaddingRight:0;--pf-c-page__main-section--PaddingBottom:0;--pf-c-page__main-section--PaddingLeft:0}}@media (min-width:1450px){.pf-c-page__main-section.pf-m-padding-on-2xl{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__main-section.pf-m-no-padding-on-2xl{--pf-c-page__main-section--PaddingTop:0;--pf-c-page__main-section--PaddingRight:0;--pf-c-page__main-section--PaddingBottom:0;--pf-c-page__main-section--PaddingLeft:0}}.pf-c-page__main-wizard{flex-grow:1;background-color:var(--pf-c-page__main-wizard--BackgroundColor);border-top:var(--pf-c-page__main-wizard--BorderTopWidth) solid var(--pf-c-page__main-wizard--BorderTopColor)}.pf-c-page__main-group{flex-shrink:0}.pf-c-page__main-nav .pf-c-page__main-body{padding-top:var(--pf-c-page__main-nav--PaddingTop);padding-right:var(--pf-c-page__main-nav--PaddingRight);padding-left:var(--pf-c-page__main-nav--PaddingLeft)}.pf-c-page__main-breadcrumb .pf-c-page__main-body{padding:var(--pf-c-page__main-breadcrumb--PaddingTop) var(--pf-c-page__main-breadcrumb--PaddingRight) var(--pf-c-page__main-breadcrumb--PaddingBottom) var(--pf-c-page__main-breadcrumb--PaddingLeft)}.pf-c-page__main-section .pf-c-page__main-body{padding:var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft)}.pf-c-page__drawer{grid-area:main}.pf-c-page__drawer>.pf-c-drawer{flex:1 0 auto}.pf-c-pagination{--pf-c-pagination--child--MarginRight:var(--pf-global--spacer--lg);--pf-c-pagination--m-bottom--child--MarginRight:0;--pf-c-pagination--m-bottom--child--md--MarginRight:var(--pf-global--spacer--lg);--pf-c-pagination--m-compact--child--MarginRight:var(--pf-global--spacer--sm);--pf-c-pagination--c-options-menu__toggle--FontSize:var(--pf-global--FontSize--sm);--pf-c-pagination__nav--Display:none;--pf-c-pagination__nav--Visibility:hidden;--pf-c-pagination--m-display-summary__nav--Display:none;--pf-c-pagination--m-display-summary__nav--Visibility:hidden;--pf-c-pagination--m-display-full__nav--Display:inline-flex;--pf-c-pagination--m-display-full__nav--Visibility:visible;--pf-c-pagination__nav-control--c-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-pagination__nav-control--c-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-pagination__nav-control--c-button--FontSize:var(--pf-global--FontSize--md);--pf-c-pagination--m-bottom__nav-control--c-button--OutlineOffset:calc(var(--pf-global--spacer--xs)*-1);--pf-c-pagination--m-bottom__nav-control--c-button--PaddingTop:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom__nav-control--c-button--PaddingBottom:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingRight:var(--pf-global--spacer--sm);--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-pagination--m-compact__nav-control--nav-control--MarginLeft:var(--pf-global--spacer--md);--pf-c-pagination__nav-page-select--FontSize:var(--pf-global--FontSize--sm);--pf-c-pagination__nav-page-select--PaddingLeft:var(--pf-global--spacer--md);--pf-c-pagination__nav-page-select--PaddingRight:var(--pf-global--spacer--md);--pf-c-pagination__nav-page-select--child--MarginRight:var(--pf-global--spacer--xs);--pf-c-pagination__nav-page-select--c-form-control--width-base:3.5ch;--pf-c-pagination__nav-page-select--c-form-control--width-chars:2;--pf-c-pagination__nav-page-select--c-form-control--Width:calc(var(--pf-c-pagination__nav-page-select--c-form-control--width-base) + var(--pf-c-pagination__nav-page-select--c-form-control--width-chars)*1ch);--pf-c-pagination__total-items--Display:block;--pf-c-pagination__total-items--Visibility:visible;--pf-c-pagination--m-display-summary__total-items--Display:block;--pf-c-pagination--m-display-summary__total-items--Visibility:visible;--pf-c-pagination--m-display-full__total-items--Display:none;--pf-c-pagination--m-display-full__total-items--Visibility:hidden;--pf-c-pagination--m-sticky--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-pagination--m-sticky--BoxShadow:var(--pf-global--BoxShadow--sm-bottom);--pf-c-pagination--m-sticky--md--PaddingTop:var(--pf-global--spacer--md);--pf-c-pagination--m-sticky--md--PaddingRight:var(--pf-global--spacer--md);--pf-c-pagination--m-sticky--md--PaddingBottom:var(--pf-global--spacer--md);--pf-c-pagination--m-sticky--md--PaddingLeft:var(--pf-global--spacer--md);--pf-c-pagination--m-sticky--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-pagination--m-sticky--Top:0;--pf-c-pagination--m-bottom--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-pagination--m-bottom--BoxShadow:var(--pf-global--BoxShadow--sm-top);--pf-c-pagination--m-bottom--Bottom:0;--pf-c-pagination--m-bottom--md--PaddingTop:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom--md--PaddingRight:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom--md--PaddingBottom:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom--md--PaddingLeft:var(--pf-global--spacer--md);--pf-c-pagination--m-bottom--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-pagination--m-bottom--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-pagination--m-bottom--m-sticky--BoxShadow:var(--pf-global--BoxShadow--sm-top);--pf-c-pagination--c-options-menu--Display:none;--pf-c-pagination--c-options-menu--Visibility:hidden;--pf-c-pagination--m-display-summary--c-options-menu--Display:none;--pf-c-pagination--m-display-summary--c-options-menu--Visibility:hidden;--pf-c-pagination--m-display-full--c-options-menu--Display:inline-flex;--pf-c-pagination--m-display-full--c-options-menu--Visibility:visible;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end}@media screen and (min-width:768px){.pf-c-pagination{--pf-c-pagination--m-bottom__nav-control--c-button--PaddingTop:var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingTop);--pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight:var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingRight);--pf-c-pagination--m-bottom__nav-control--c-button--PaddingBottom:var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingBottom);--pf-c-pagination--m-bottom__nav-control--c-button--PaddingLeft:var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingLeft);--pf-c-pagination--m-bottom--child--MarginRight:var(--pf-c-pagination--m-bottom--child--md--MarginRight);--pf-c-pagination--m-bottom__nav-control--c-button--OutlineOffset:0;--pf-c-pagination--m-bottom--BoxShadow:none;--pf-c-pagination--c-options-menu--Display:inline-flex;--pf-c-pagination--c-options-menu--Visibility:visible;--pf-c-pagination__nav--Display:inline-flex;--pf-c-pagination__nav--Visibility:visible;--pf-c-pagination__total-items--Display:none;--pf-c-pagination__total-items--Visibility:hidden}}@media screen and (min-width:1200px){.pf-c-pagination{--pf-c-pagination--m-bottom--md--PaddingRight:var(--pf-c-pagination--m-bottom--xl--PaddingRight);--pf-c-pagination--m-bottom--md--PaddingLeft:var(--pf-c-pagination--m-bottom--xl--PaddingLeft)}}.pf-c-pagination>:not(:last-child):not(.pf-c-pagination__total-items){margin-right:var(--pf-c-pagination--child--MarginRight)}.pf-c-pagination .pf-c-options-menu{display:var(--pf-c-pagination--c-options-menu--Display);visibility:var(--pf-c-pagination--c-options-menu--Visibility)}.pf-c-pagination.pf-m-bottom{--pf-c-pagination--child--MarginRight:var(--pf-c-pagination--m-bottom--child--MarginRight);--pf-c-pagination__nav-control--c-button--PaddingRight:var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight);--pf-c-pagination__nav-control--c-button--PaddingLeft:var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight);--pf-c-pagination--m-sticky--BoxShadow:var(--pf-c-pagination--m-bottom--m-sticky--BoxShadow);--pf-c-pagination--m-sticky--Top:auto;position:sticky;bottom:var(--pf-c-pagination--m-bottom--Bottom);justify-content:center;background-color:var(--pf-c-pagination--m-bottom--BackgroundColor);box-shadow:var(--pf-c-pagination--m-bottom--BoxShadow)}.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control .pf-c-button{--pf-c-button--PaddingTop:var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingTop);--pf-c-button--PaddingBottom:var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingBottom);outline-offset:var(--pf-c-pagination--m-bottom__nav-control--c-button--OutlineOffset)}.pf-c-pagination.pf-m-bottom.pf-m-static{--pf-c-pagination--m-bottom--MarginTop:0;--pf-c-pagination--m-bottom--BorderTopWidth:0;position:relative;box-shadow:none}.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-first,.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-last,.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-page-select{display:none;visibility:hidden}.pf-c-pagination.pf-m-bottom .pf-c-options-menu{position:absolute;display:block;visibility:visible}.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav{display:flex;flex-basis:100%;justify-content:space-between;visibility:visible}@media screen and (min-width:768px){.pf-c-pagination.pf-m-bottom{--pf-c-pagination--m-bottom--BorderTopWidth:0;--pf-c-pagination--m-bottom--MarginTop:0;--pf-c-pagination--m-bottom--Bottom:auto;position:relative;justify-content:flex-end;padding:var(--pf-c-pagination--m-bottom--md--PaddingTop) var(--pf-c-pagination--m-bottom--md--PaddingRight) var(--pf-c-pagination--m-bottom--md--PaddingBottom) var(--pf-c-pagination--m-bottom--md--PaddingLeft)}.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-first,.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-last,.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-page-select{display:block;visibility:visible}.pf-c-pagination.pf-m-bottom .pf-c-options-menu{position:relative}.pf-c-pagination.pf-m-bottom .pf-c-pagination__nav{display:inline-flex;flex-basis:auto}}.pf-c-pagination.pf-m-sticky{--pf-c-pagination--m-bottom--Bottom:0;position:sticky;top:var(--pf-c-pagination--m-sticky--Top);z-index:var(--pf-c-pagination--m-sticky--ZIndex);padding:var(--pf-c-pagination--m-sticky--PaddingTop) var(--pf-c-pagination--m-sticky--PaddingRight) var(--pf-c-pagination--m-sticky--PaddingBottom) var(--pf-c-pagination--m-sticky--PaddingLeft);background-color:var(--pf-c-pagination--m-sticky--BackgroundColor);box-shadow:var(--pf-c-pagination--m-sticky--BoxShadow)}@media screen and (min-width:768px){.pf-c-pagination.pf-m-sticky{padding:var(--pf-c-pagination--m-sticky--md--PaddingTop) var(--pf-c-pagination--m-sticky--md--PaddingRight) var(--pf-c-pagination--m-sticky--md--PaddingBottom) var(--pf-c-pagination--m-sticky--md--PaddingLeft)}}.pf-c-pagination .pf-c-options-menu__toggle{font-size:var(--pf-c-pagination--c-options-menu__toggle--FontSize)}.pf-c-pagination.pf-m-compact{--pf-c-pagination--child--MarginRight:var(--pf-c-pagination--m-compact--child--MarginRight)}.pf-c-pagination__nav{display:var(--pf-c-pagination__nav--Display);justify-content:flex-end;visibility:var(--pf-c-pagination__nav--Visibility)}.pf-c-pagination__nav-control .pf-c-button{padding-right:var(--pf-c-pagination__nav-control--c-button--PaddingRight);padding-left:var(--pf-c-pagination__nav-control--c-button--PaddingLeft);font-size:var(--pf-c-pagination__nav-control--c-button--FontSize)}.pf-c-pagination.pf-m-compact .pf-c-pagination__nav-control+.pf-c-pagination__nav-control{margin-left:var(--pf-c-pagination--m-compact__nav-control--nav-control--MarginLeft)}.pf-c-pagination__nav-page-select{display:flex;align-items:center;padding-right:var(--pf-c-pagination__nav-page-select--PaddingRight);padding-left:var(--pf-c-pagination__nav-page-select--PaddingLeft)}.pf-c-pagination__nav-page-select>*{font-size:var(--pf-c-pagination__nav-page-select--FontSize);white-space:nowrap}.pf-c-pagination__nav-page-select>:not(:last-child){margin-right:var(--pf-c-pagination__nav-page-select--child--MarginRight)}.pf-c-pagination__nav-page-select .pf-c-form-control{width:var(--pf-c-pagination__nav-page-select--c-form-control--Width)}.pf-c-pagination__total-items{display:var(--pf-c-pagination__total-items--Display);visibility:var(--pf-c-pagination__total-items--Visibility)}.pf-c-pagination.pf-m-display-summary{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-summary__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-summary__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-summary--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-summary__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-summary__total-items--Visibility)}.pf-c-pagination.pf-m-display-full{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-full__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-full__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-full--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-full__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-full__total-items--Visibility)}@media (min-width:576px){.pf-c-pagination.pf-m-display-summary-on-sm{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-summary__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-summary__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-summary--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-summary__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-summary__total-items--Visibility)}.pf-c-pagination.pf-m-display-full-on-sm{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-full__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-full__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-full--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-full__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-full__total-items--Visibility)}}@media (min-width:768px){.pf-c-pagination.pf-m-display-summary-on-md{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-summary__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-summary__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-summary--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-summary__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-summary__total-items--Visibility)}.pf-c-pagination.pf-m-display-full-on-md{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-full__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-full__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-full--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-full__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-full__total-items--Visibility)}}@media (min-width:992px){.pf-c-pagination.pf-m-display-summary-on-lg{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-summary__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-summary__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-summary--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-summary__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-summary__total-items--Visibility)}.pf-c-pagination.pf-m-display-full-on-lg{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-full__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-full__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-full--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-full__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-full__total-items--Visibility)}}@media (min-width:1200px){.pf-c-pagination.pf-m-display-summary-on-xl{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-summary__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-summary__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-summary--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-summary__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-summary__total-items--Visibility)}.pf-c-pagination.pf-m-display-full-on-xl{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-full__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-full__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-full--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-full__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-full__total-items--Visibility)}}@media (min-width:1450px){.pf-c-pagination.pf-m-display-summary-on-2xl{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-summary__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-summary__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-summary--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-summary__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-summary__total-items--Visibility)}.pf-c-pagination.pf-m-display-full-on-2xl{--pf-c-pagination__nav--Display:var(--pf-c-pagination--m-display-full__nav--Display);--pf-c-pagination__nav--Visibility:var(--pf-c-pagination--m-display-full__nav--Visibility);--pf-c-pagination--c-options-menu--Display:var(--pf-c-pagination--m-display-full--c-options-menu--Display);--pf-c-pagination--c-options-menu--Visibility:var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);--pf-c-pagination__total-items--Display:var(--pf-c-pagination--m-display-full__total-items--Display);--pf-c-pagination__total-items--Visibility:var(--pf-c-pagination--m-display-full__total-items--Visibility)}}.pf-c-popover{--pf-c-popover--FontSize:var(--pf-global--FontSize--sm);--pf-c-popover--MinWidth:calc(var(--pf-c-popover__content--PaddingLeft) + var(--pf-c-popover__content--PaddingRight) + 18.75rem);--pf-c-popover--MaxWidth:calc(var(--pf-c-popover__content--PaddingLeft) + var(--pf-c-popover__content--PaddingRight) + 18.75rem);--pf-c-popover--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-popover__content--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-popover__content--PaddingTop:var(--pf-global--spacer--md);--pf-c-popover__content--PaddingRight:var(--pf-global--spacer--md);--pf-c-popover__content--PaddingBottom:var(--pf-global--spacer--md);--pf-c-popover__content--PaddingLeft:var(--pf-global--spacer--md);--pf-c-popover__arrow--Width:var(--pf-global--arrow--width-lg);--pf-c-popover__arrow--Height:var(--pf-global--arrow--width-lg);--pf-c-popover__arrow--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-popover__arrow--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-popover__arrow--m-top--TranslateX:-50%;--pf-c-popover__arrow--m-top--TranslateY:50%;--pf-c-popover__arrow--m-top--Rotate:45deg;--pf-c-popover__arrow--m-right--TranslateX:-50%;--pf-c-popover__arrow--m-right--TranslateY:-50%;--pf-c-popover__arrow--m-right--Rotate:45deg;--pf-c-popover__arrow--m-bottom--TranslateX:-50%;--pf-c-popover__arrow--m-bottom--TranslateY:-50%;--pf-c-popover__arrow--m-bottom--Rotate:45deg;--pf-c-popover__arrow--m-left--TranslateX:50%;--pf-c-popover__arrow--m-left--TranslateY:-50%;--pf-c-popover__arrow--m-left--Rotate:45deg;--pf-c-popover--c-button--MarginLeft:var(--pf-global--spacer--sm);--pf-c-popover--c-button--Top:calc(var(--pf-c-popover__content--PaddingTop) - var(--pf-global--spacer--form-element));--pf-c-popover--c-button--Right:var(--pf-global--spacer--md);--pf-c-popover--c-button--sibling--PaddingRight:var(--pf-global--spacer--2xl);--pf-c-popover--c-title--MarginBottom:var(--pf-global--spacer--sm);--pf-c-popover__footer--MarginTop:var(--pf-global--spacer--md);position:relative;min-width:var(--pf-c-popover--MinWidth);max-width:var(--pf-c-popover--MaxWidth);font-size:var(--pf-c-popover--FontSize);box-shadow:var(--pf-c-popover--BoxShadow)}.pf-c-popover.pf-m-no-padding{--pf-c-popover__content--PaddingTop:0px;--pf-c-popover__content--PaddingRight:0px;--pf-c-popover__content--PaddingBottom:0px;--pf-c-popover__content--PaddingLeft:0px}.pf-c-popover.pf-m-width-auto{--pf-c-popover--MinWidth:auto;--pf-c-popover--MaxWidth:none}.pf-c-popover.pf-m-top .pf-c-popover__arrow{bottom:0;left:50%;transform:translateX(var(--pf-c-popover__arrow--m-top--TranslateX)) translateY(var(--pf-c-popover__arrow--m-top--TranslateY)) rotate(var(--pf-c-popover__arrow--m-top--Rotate))}.pf-c-popover.pf-m-bottom .pf-c-popover__arrow{top:0;left:50%;transform:translateX(var(--pf-c-popover__arrow--m-bottom--TranslateX)) translateY(var(--pf-c-popover__arrow--m-bottom--TranslateY)) rotate(var(--pf-c-popover__arrow--m-bottom--Rotate))}.pf-c-popover.pf-m-left .pf-c-popover__arrow{top:50%;right:0;transform:translateX(var(--pf-c-popover__arrow--m-left--TranslateX)) translateY(var(--pf-c-popover__arrow--m-left--TranslateY)) rotate(var(--pf-c-popover__arrow--m-left--Rotate))}.pf-c-popover.pf-m-right .pf-c-popover__arrow{top:50%;left:0;transform:translateX(var(--pf-c-popover__arrow--m-right--TranslateX)) translateY(var(--pf-c-popover__arrow--m-right--TranslateY)) rotate(var(--pf-c-popover__arrow--m-right--Rotate))}.pf-c-popover__content{position:relative;padding:var(--pf-c-popover__content--PaddingTop) var(--pf-c-popover__content--PaddingRight) var(--pf-c-popover__content--PaddingBottom) var(--pf-c-popover__content--PaddingLeft);background-color:var(--pf-c-popover__content--BackgroundColor)}.pf-c-popover__content>.pf-c-title{margin-bottom:var(--pf-c-popover--c-title--MarginBottom)}.pf-c-popover__content>.pf-c-button{position:absolute;top:var(--pf-c-popover--c-button--Top);right:var(--pf-c-popover--c-button--Right)}.pf-c-popover__content>.pf-c-button+*{padding-right:var(--pf-c-popover--c-button--sibling--PaddingRight)}.pf-c-popover__arrow{position:absolute;width:var(--pf-c-popover__arrow--Width);height:var(--pf-c-popover__arrow--Height);pointer-events:none;background-color:var(--pf-c-popover__arrow--BackgroundColor);box-shadow:var(--pf-c-popover__arrow--BoxShadow)}.pf-c-popover__body{word-wrap:break-word}.pf-c-popover__footer{margin-top:var(--pf-c-popover__footer--MarginTop)}.pf-c-progress{--pf-c-progress--GridGap:var(--pf-global--spacer--md);--pf-c-progress__bar--before--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-progress__bar--Height:var(--pf-global--spacer--md);--pf-c-progress__bar--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-progress__measure--m-static-width--MinWidth:4.5ch;--pf-c-progress__status-icon--Color:var(--pf-global--Color--100);--pf-c-progress__status-icon--MarginLeft:var(--pf-global--spacer--sm);--pf-c-progress__bar--before--Opacity:.2;--pf-c-progress__indicator--Height:var(--pf-c-progress__bar--Height);--pf-c-progress__indicator--BackgroundColor:var(--pf-c-progress__bar--before--BackgroundColor);--pf-c-progress--m-success__bar--BackgroundColor:var(--pf-global--success-color--100);--pf-c-progress--m-warning__bar--BackgroundColor:var(--pf-global--warning-color--100);--pf-c-progress--m-danger__bar--BackgroundColor:var(--pf-global--danger-color--100);--pf-c-progress--m-success__status-icon--Color:var(--pf-global--success-color--100);--pf-c-progress--m-warning__status-icon--Color:var(--pf-global--warning-color--100);--pf-c-progress--m-danger__status-icon--Color:var(--pf-global--danger-color--100);--pf-c-progress--m-inside__indicator--MinWidth:var(--pf-global--spacer--xl);--pf-c-progress--m-inside__measure--Color:var(--pf-global--Color--light-100);--pf-c-progress--m-success--m-inside__measure--Color:var(--pf-global--Color--light-100);--pf-c-progress--m-warning--m-inside__measure--Color:var(--pf-global--Color--dark-100);--pf-c-progress--m-inside__measure--FontSize:var(--pf-global--FontSize--sm);--pf-c-progress--m-outside__measure--FontSize:var(--pf-global--FontSize--sm);--pf-c-progress--m-sm__bar--Height:var(--pf-global--spacer--sm);--pf-c-progress--m-sm__description--FontSize:var(--pf-global--FontSize--sm);--pf-c-progress--m-sm__measure--FontSize:var(--pf-global--FontSize--sm);--pf-c-progress--m-lg__bar--Height:var(--pf-global--spacer--lg);display:grid;align-items:end;grid-gap:var(--pf-c-progress--GridGap);grid-template-columns:auto auto;grid-template-rows:1fr auto}.pf-c-progress.pf-m-sm{--pf-c-progress__bar--Height:var(--pf-c-progress--m-sm__bar--Height)}.pf-c-progress.pf-m-sm .pf-c-progress__description{font-size:var(--pf-c-progress--m-sm__description--FontSize)}.pf-c-progress.pf-m-sm .pf-c-progress__measure{font-size:var(--pf-c-progress--m-sm__measure--FontSize)}.pf-c-progress.pf-m-lg{--pf-c-progress__bar--Height:var(--pf-c-progress--m-lg__bar--Height)}.pf-c-progress.pf-m-inside .pf-c-progress__indicator{display:flex;align-items:center;justify-content:center;min-width:var(--pf-c-progress--m-inside__indicator--MinWidth)}.pf-c-progress.pf-m-inside .pf-c-progress__measure{font-size:var(--pf-c-progress--m-inside__measure--FontSize);color:var(--pf-c-progress--m-inside__measure--Color);text-align:center}.pf-c-progress.pf-m-outside .pf-c-progress__description{grid-column:1/3}.pf-c-progress.pf-m-outside .pf-c-progress__status{grid-column:2/3;grid-row:2/3;align-self:center}.pf-c-progress.pf-m-outside .pf-c-progress__measure{display:inline-block;font-size:var(--pf-c-progress--m-outside__measure--FontSize)}.pf-c-progress.pf-m-outside .pf-c-progress__measure.pf-m-static-width{min-width:var(--pf-c-progress__measure--m-static-width--MinWidth);text-align:left}.pf-c-progress.pf-m-outside .pf-c-progress__bar,.pf-c-progress.pf-m-outside .pf-c-progress__indicator{grid-column:1/2}.pf-c-progress.pf-m-singleline{grid-template-rows:1fr}.pf-c-progress.pf-m-singleline .pf-c-progress__description{display:none;visibility:hidden}.pf-c-progress.pf-m-singleline .pf-c-progress__bar{grid-row:1/2;grid-column:1/2}.pf-c-progress.pf-m-singleline .pf-c-progress__status{grid-row:1/2;grid-column:2/3}.pf-c-progress.pf-m-outside,.pf-c-progress.pf-m-singleline{grid-template-columns:1fr fit-content(50%)}.pf-c-progress.pf-m-success{--pf-c-progress__bar--before--BackgroundColor:var(--pf-c-progress--m-success__bar--BackgroundColor);--pf-c-progress__status-icon--Color:var(--pf-c-progress--m-success__status-icon--Color);--pf-c-progress--m-inside__measure--Color:var(--pf-c-progress--m-success--m-inside__measure--Color)}.pf-c-progress.pf-m-warning{--pf-c-progress__bar--before--BackgroundColor:var(--pf-c-progress--m-warning__bar--BackgroundColor);--pf-c-progress__status-icon--Color:var(--pf-c-progress--m-warning__status-icon--Color);--pf-c-progress--m-inside__measure--Color:var(--pf-c-progress--m-warning--m-inside__measure--Color)}.pf-c-progress.pf-m-danger{--pf-c-progress__bar--before--BackgroundColor:var(--pf-c-progress--m-danger__bar--BackgroundColor);--pf-c-progress__status-icon--Color:var(--pf-c-progress--m-danger__status-icon--Color)}.pf-c-progress__description{word-break:break-word;grid-column:1/2}.pf-c-progress__description.pf-m-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-c-progress__status{grid-column:2/3;grid-row:1/2;text-align:right;word-break:break-word}.pf-c-progress__status-icon{margin-left:var(--pf-c-progress__status-icon--MarginLeft);color:var(--pf-c-progress__status-icon--Color)}.pf-c-progress__bar{position:relative;grid-column:1/3;grid-row:2/3;align-self:center;height:var(--pf-c-progress__bar--Height);background-color:var(--pf-c-progress__bar--BackgroundColor)}.pf-c-progress__bar:before{position:absolute;top:0;left:0;width:100%;height:100%;content:"";background-color:var(--pf-c-progress__bar--before--BackgroundColor);opacity:var(--pf-c-progress__bar--before--Opacity)}.pf-c-progress__indicator{position:absolute;top:0;left:0;height:var(--pf-c-progress__indicator--Height);background-color:var(--pf-c-progress__indicator--BackgroundColor)}.pf-c-radio{--pf-c-radio--GridGap:var(--pf-global--spacer--xs) var(--pf-global--spacer--sm);--pf-c-radio__label--disabled--Color:var(--pf-global--disabled-color--100);--pf-c-radio__label--Color:var(--pf-global--Color--100);--pf-c-radio__label--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-radio__label--FontSize:var(--pf-global--FontSize--md);--pf-c-radio__label--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-radio__input--MarginTop:-0.1875rem;--pf-c-radio__input--first-child--MarginLeft:0.0625rem;--pf-c-radio__input--last-child--MarginRight:0.0625rem;--pf-c-radio__description--FontSize:var(--pf-global--FontSize--sm);--pf-c-radio__description--Color:var(--pf-global--Color--200);display:grid;grid-template-columns:auto 1fr;grid-gap:var(--pf-c-radio--GridGap);align-items:center;justify-items:start}.pf-c-radio__label{font-size:var(--pf-c-radio__label--FontSize);font-weight:var(--pf-c-radio__label--FontWeight);line-height:var(--pf-c-radio__label--LineHeight);color:var(--pf-c-radio__label--Color)}.pf-c-radio__input{margin-top:var(--pf-c-radio__input--MarginTop)}.pf-c-radio__input:first-child{margin-left:var(--pf-c-radio__input--first-child--MarginLeft)}.pf-c-radio__input:last-child{margin-right:var(--pf-c-radio__input--last-child--MarginRight)}.pf-c-radio__description{grid-column:2;font-size:var(--pf-c-radio__description--FontSize);color:var(--pf-c-radio__description--Color)}.pf-c-radio__input,.pf-c-radio__label,label.pf-c-radio{cursor:pointer}.pf-c-radio__input.pf-m-disabled,.pf-c-radio__input:disabled,.pf-c-radio__label.pf-m-disabled,.pf-c-radio__label:disabled{--pf-c-radio__label--Color:var(--pf-c-radio__label--disabled--Color);cursor:not-allowed}.pf-c-search-input{--pf-c-search-input__text--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-search-input__text--before--BorderColor:var(--pf-global--BorderColor--300);--pf-c-search-input__text--after--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-search-input__text--after--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-search-input--hover__text--after--BorderBottomColor:var(--pf-global--primary-color--100);--pf-c-search-input__text--focus-within--after--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-search-input__text--focus-within--after--BorderBottomColor:var(--pf-global--primary-color--100);--pf-c-search-input__text-input--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-search-input__text-input--PaddingRight:var(--pf-global--spacer--sm);--pf-c-search-input__text-input--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-search-input__text-input--PaddingLeft:var(--pf-global--spacer--xl);--pf-c-search-input__text-input--MinWidth:6ch;--pf-c-search-input__icon--Left:var(--pf-global--spacer--sm);--pf-c-search-input__icon--Color:var(--pf-global--Color--200);--pf-c-search-input__text--hover__icon--Color:var(--pf-global--Color--100);--pf-c-search-input__icon--TranslateY:-50%;--pf-c-search-input__utilities--MarginRight:var(--pf-global--spacer--sm);--pf-c-search-input__utilities--MarginLeft:var(--pf-global--spacer--xs);--pf-c-search-input__utilities--child--MarginLeft:var(--pf-global--spacer--xs);--pf-c-search-input__utilities--c-button--PaddingRight:var(--pf-global--spacer--xs);--pf-c-search-input__utilities--c-button--PaddingLeft:var(--pf-global--spacer--xs);position:relative;display:flex;padding:var(--pf-c-search-input--PaddingTop) var(--pf-c-search-input--PaddingRight) var(--pf-c-search-input--PaddingBottom) var(--pf-c-search-input--PaddingLeft)}.pf-c-search-input:hover{--pf-c-search-input__text--after--BorderBottomColor:var(--pf-c-search-input--hover__text--after--BorderBottomColor)}.pf-c-search-input__text{flex:1}.pf-c-search-input__text:after,.pf-c-search-input__text:before{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;content:""}.pf-c-search-input__text:before{border:var(--pf-c-search-input__text--before--BorderWidth) solid var(--pf-c-search-input__text--before--BorderColor)}.pf-c-search-input__text:after{border-bottom:var(--pf-c-search-input__text--after--BorderBottomWidth) solid var(--pf-c-search-input__text--after--BorderBottomColor)}.pf-c-search-input__text:focus-within,.pf-c-search-input__text:hover{--pf-c-search-input__icon--Color:var(--pf-c-search-input__text--hover__icon--Color)}.pf-c-search-input__text:focus-within{--pf-c-search-input__text--after--BorderBottomWidth:var(--pf-c-search-input__text--focus-within--after--BorderBottomWidth);--pf-c-search-input__text--after--BorderBottomColor:var(--pf-c-search-input__text--focus-within--after--BorderBottomColor)}.pf-c-search-input__icon{position:absolute;top:50%;left:var(--pf-c-search-input__icon--Left);color:var(--pf-c-search-input__icon--Color);transform:translateY(var(--pf-c-search-input__icon--TranslateY))}.pf-c-search-input__text-input{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:relative;width:100%;min-width:var(--pf-c-search-input__text-input--MinWidth);padding:var(--pf-c-search-input__text-input--PaddingTop) var(--pf-c-search-input__text-input--PaddingRight) var(--pf-c-search-input__text-input--PaddingBottom) var(--pf-c-search-input__text-input--PaddingLeft);border:0}.pf-c-search-input__utilities{display:flex;margin-right:var(--pf-c-search-input__utilities--MarginRight);margin-left:var(--pf-c-search-input__utilities--MarginLeft)}.pf-c-search-input__utilities>*+*{margin-left:var(--pf-c-search-input__utilities--child--MarginLeft)}.pf-c-search-input__utilities .pf-c-button{--pf-c-button--PaddingRight:var(--pf-c-search-input__utilities--c-button--PaddingRight);--pf-c-button--PaddingLeft:var(--pf-c-search-input__utilities--c-button--PaddingLeft)}.pf-c-search-input__nav{display:flex}.pf-c-search-input__count{display:flex;align-items:center}.pf-c-select{color:var(--pf-global--Color--100);--pf-c-select__toggle--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-select__toggle--PaddingRight:var(--pf-global--spacer--sm);--pf-c-select__toggle--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-select__toggle--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-select__toggle--MinWidth:var(--pf-global--target-size--MinWidth);--pf-c-select__toggle--FontSize:var(--pf-global--FontSize--md);--pf-c-select__toggle--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-select__toggle--LineHeight:var(--pf-global--LineHeight--md);--pf-c-select__toggle--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-select__toggle--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-select__toggle--before--BorderTopColor:var(--pf-global--BorderColor--300);--pf-c-select__toggle--before--BorderRightColor:var(--pf-global--BorderColor--300);--pf-c-select__toggle--before--BorderBottomColor:var(--pf-global--BorderColor--200);--pf-c-select__toggle--before--BorderLeftColor:var(--pf-global--BorderColor--300);--pf-c-select__toggle--Color:var(--pf-global--Color--100);--pf-c-select__toggle--hover--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-select__toggle--focus--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-select__toggle--active--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-select__toggle--m-expanded--before--BorderBottomColor:var(--pf-global--active-color--100);--pf-c-select__toggle--focus--before--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-select__toggle--active--before--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-select__toggle--m-expanded--before--BorderBottomWidth:var(--pf-global--BorderWidth--md);--pf-c-select__toggle--disabled--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-select__toggle--m-plain--before--BorderColor:transparent;--pf-c-select__toggle-wrapper--not-last-child--MarginRight:var(--pf-global--spacer--xs);--pf-c-select__toggle-wrapper--MaxWidth:calc(100% - var(--pf-global--spacer--lg));--pf-c-select__toggle-wrapper--c-chip-group--MarginTop:0.3125rem;--pf-c-select__toggle-wrapper--c-chip-group--MarginBottom:0.3125rem;--pf-c-select__toggle-typeahead--FlexBasis:10em;--pf-c-select__toggle-typeahead--BackgroundColor:transparent;--pf-c-select__toggle-typeahead--BorderTop:none;--pf-c-select__toggle-typeahead--BorderRight:none;--pf-c-select__toggle-typeahead--BorderLeft:none;--pf-c-select__toggle-typeahead--MinWidth:7.5rem;--pf-c-select__toggle-typeahead--focus--PaddingBottom:calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));--pf-c-select__toggle-icon--toggle-text--MarginLeft:var(--pf-global--spacer--xs);--pf-c-select__toggle-badge--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-select__toggle-arrow--MarginLeft:var(--pf-global--spacer--md);--pf-c-select__toggle-arrow--MarginRight:var(--pf-global--spacer--sm);--pf-c-select__toggle-arrow--with-clear--MarginLeft:var(--pf-global--spacer--sm);--pf-c-select__toggle-arrow--m-top--m-expanded__toggle-arrow--Rotate:180deg;--pf-c-select__toggle-clear--PaddingRight:var(--pf-global--spacer--sm);--pf-c-select__toggle-clear--PaddingLeft:var(--pf-global--spacer--md);--pf-c-select__toggle-clear--toggle-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-select__toggle-button--Color:var(--pf-global--Color--100);--pf-c-select__menu--BackgroundColor:var(--pf-global--BackgroundColor--light-100);--pf-c-select__menu--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-select__menu--PaddingTop:var(--pf-global--spacer--sm);--pf-c-select__menu--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-select__menu--Top:calc(100% + var(--pf-global--spacer--xs));--pf-c-select__menu--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-select__menu--m-top--TranslateY:calc(-100% - var(--pf-global--spacer--xs));--pf-c-select__menu-item--PaddingTop:var(--pf-global--spacer--sm);--pf-c-select__menu-item--PaddingRight:var(--pf-global--spacer--md);--pf-c-select__menu-item--m-selected--PaddingRight:var(--pf-global--spacer--2xl);--pf-c-select__menu-item--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-select__menu-item--PaddingLeft:var(--pf-global--spacer--md);--pf-c-select__menu-item--FontSize:var(--pf-global--FontSize--md);--pf-c-select__menu-item--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-select__menu-item--LineHeight:var(--pf-global--LineHeight--md);--pf-c-select__menu-item--Color:var(--pf-global--Color--dark-100);--pf-c-select__menu-item--Width:100%;--pf-c-select__menu-item--disabled--Color:var(--pf-global--Color--dark-200);--pf-c-select__menu-item--hover--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-select__menu-item--focus--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-select__menu-item--disabled--BackgroundColor:transparent;--pf-c-select__menu-item--m-link--Width:auto;--pf-c-select__menu-item--m-link--hover--BackgroundColor:transparent;--pf-c-select__menu-item--m-link--focus--BackgroundColor:transparent;--pf-c-select__menu-item--m-action--Color:var(--pf-global--disabled-color--200);--pf-c-select__menu-item--m-action--Width:auto;--pf-c-select__menu-item--m-action--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-select__menu-item--m-action--hover--BackgroundColor:transparent;--pf-c-select__menu-item--m-action--focus--BackgroundColor:transparent;--pf-c-select__menu-item--hover__menu-item--m-action--Color:var(--pf-global--Color--200);--pf-c-select__menu-item--m-action--hover--Color:var(--pf-global--Color--100);--pf-c-select__menu-item--m-action--focus--Color:var(--pf-global--Color--100);--pf-c-select__menu-wrapper--m-favorite__menu-item--m-favorite-action--Color:var(--pf-global--palette--gold-400);--pf-c-select__menu-item-icon--Color:var(--pf-global--active-color--100);--pf-c-select__menu-item-icon--FontSize:var(--pf-global--icon--FontSize--sm);--pf-c-select__menu-item-icon--Right:var(--pf-global--spacer--md);--pf-c-select__menu-item-icon--Top:50%;--pf-c-select__menu-item-icon--TranslateY:-50%;--pf-c-select__menu-item-action-icon--MinHeight:calc(var(--pf-c-select__menu-item--FontSize)*var(--pf-c-select__menu-item--LineHeight));--pf-c-select__menu-item--match--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-select__menu-search--PaddingTop:var(--pf-global--spacer--sm);--pf-c-select__menu-search--PaddingRight:var(--pf-c-select__menu-item--PaddingRight);--pf-c-select__menu-search--PaddingBottom:var(--pf-global--spacer--md);--pf-c-select__menu-search--PaddingLeft:var(--pf-c-select__menu-item--PaddingLeft);--pf-c-select__menu-group--menu-group--PaddingTop:var(--pf-global--spacer--sm);--pf-c-select__menu-group-title--PaddingTop:var(--pf-c-select__menu-item--PaddingTop);--pf-c-select__menu-group-title--PaddingRight:var(--pf-c-select__menu-item--PaddingRight);--pf-c-select__menu-group-title--PaddingBottom:var(--pf-c-select__menu-item--PaddingBottom);--pf-c-select__menu-group-title--PaddingLeft:var(--pf-c-select__menu-item--PaddingLeft);--pf-c-select__menu-group-title--FontSize:var(--pf-global--FontSize--sm);--pf-c-select__menu-group-title--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-select__menu-group-title--Color:var(--pf-global--Color--dark-200);--pf-c-select__menu-item-description--FontSize:var(--pf-global--FontSize--xs);--pf-c-select__menu-item-description--Color:var(--pf-global--Color--200);--pf-c-select__menu-item-description--PaddingRight:var(--pf-c-select__menu-item--PaddingRight);--pf-c-select__menu-item-main--PaddingRight:var(--pf-c-select__menu-item--PaddingRight);--pf-c-select__menu-item--m-selected__menu-item-main--PaddingRight:var(--pf-c-select__menu-item--m-selected--PaddingRight);--pf-c-select-menu--c-divider--MarginTop:var(--pf-global--spacer--sm);--pf-c-select-menu--c-divider--MarginBottom:var(--pf-global--spacer--sm);position:relative;display:inline-block;width:100%}.pf-c-select .pf-c-divider{margin-top:var(--pf-c-select-menu--c-divider--MarginTop);margin-bottom:var(--pf-c-select-menu--c-divider--MarginBottom)}.pf-c-select .pf-c-divider:last-child{--pf-c-select-menu--c-divider--MarginBottom:0}.pf-c-select__menu-search+.pf-c-divider{--pf-c-select-menu--c-divider--MarginTop:0}.pf-c-select__toggle{position:relative;display:flex;align-items:center;justify-content:space-between;width:100%;min-width:var(--pf-c-select__toggle--MinWidth);padding:var(--pf-c-select__toggle--PaddingTop) var(--pf-c-select__toggle--PaddingRight) var(--pf-c-select__toggle--PaddingBottom) var(--pf-c-select__toggle--PaddingLeft);font-size:var(--pf-c-select__toggle--FontSize);font-weight:var(--pf-c-select__toggle--FontWeight);line-height:var(--pf-c-select__toggle--LineHeight);color:var(--pf-c-select__toggle--Color);white-space:nowrap;cursor:pointer;background-color:var(--pf-c-select__toggle--BackgroundColor);border:none}.pf-c-select__toggle.pf-m-disabled,.pf-c-select__toggle:disabled{--pf-c-select__toggle--BackgroundColor:var(--pf-c-select__toggle--disabled--BackgroundColor);pointer-events:none}.pf-c-select__toggle.pf-m-disabled:before,.pf-c-select__toggle:disabled:before{border:0}.pf-c-select__toggle:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";border:var(--pf-c-select__toggle--before--BorderWidth) solid;border-color:var(--pf-c-select__toggle--before--BorderTopColor) var(--pf-c-select__toggle--before--BorderRightColor) var(--pf-c-select__toggle--before--BorderBottomColor) var(--pf-c-select__toggle--before--BorderLeftColor)}.pf-c-select__toggle:hover:before{--pf-c-select__toggle--before--BorderBottomColor:var(--pf-c-select__toggle--hover--before--BorderBottomColor)}.pf-c-select__toggle:focus-within:before,.pf-c-select__toggle:focus:before{--pf-c-select__toggle--before--BorderBottomColor:var(--pf-c-select__toggle--focus--before--BorderBottomColor);border-bottom-width:var(--pf-c-select__toggle--focus--before--BorderBottomWidth)}.pf-c-select__toggle.pf-m-active:before,.pf-c-select__toggle:active:before{--pf-c-select__toggle--before--BorderBottomColor:var(--pf-c-select__toggle--active--before--BorderBottomColor);border-bottom-width:var(--pf-c-select__toggle--active--before--BorderBottomWidth)}.pf-m-expanded>.pf-c-select__toggle:before{--pf-c-select__toggle--before--BorderBottomColor:var(--pf-c-select__toggle--m-expanded--before--BorderBottomColor);border-bottom-width:var(--pf-c-select__toggle--m-expanded--before--BorderBottomWidth)}.pf-c-select__toggle.pf-m-plain:before{border-color:var(--pf-c-select__toggle--m-plain--before--BorderColor)}.pf-c-select__toggle.pf-m-typeahead{--pf-c-select__toggle--PaddingTop:0;--pf-c-select__toggle--PaddingRight:0;--pf-c-select__toggle--PaddingBottom:0}.pf-c-select__toggle.pf-m-typeahead .pf-c-form-control{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;position:relative;height:auto}.pf-c-select__toggle .pf-c-select__toggle-clear{padding-right:var(--pf-c-select__toggle-clear--PaddingRight);padding-left:var(--pf-c-select__toggle-clear--PaddingLeft);margin-left:auto}.pf-c-select__toggle .pf-c-select__toggle-button{color:var(--pf-c-select__toggle-button--Color)}.pf-c-select__toggle .pf-c-select__toggle-clear+.pf-c-select__toggle-button{padding-left:var(--pf-c-select__toggle-clear--toggle-button--PaddingLeft)}*+.pf-c-select__toggle-arrow{margin-right:var(--pf-c-select__toggle-arrow--MarginRight);margin-left:var(--pf-c-select__toggle-arrow--MarginLeft)}.pf-c-select.pf-m-top.pf-m-expanded .pf-c-select__toggle-arrow{transform:rotate(var(--pf-c-select__toggle-arrow--m-top--m-expanded__toggle-arrow--Rotate))}.pf-c-select__toggle-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-c-select__toggle-wrapper{display:flex;flex:1;flex-wrap:wrap;align-items:center;justify-content:flex-start;min-width:0;max-width:var(--pf-c-select__toggle-wrapper--MaxWidth);white-space:normal}.pf-c-select__toggle-wrapper>:not(:last-child){margin-right:var(--pf-c-select__toggle-wrapper--not-last-child--MarginRight)}.pf-c-select__toggle-wrapper>.pf-c-form-control{margin-top:calc(-1*var(--pf-c-select__toggle-wrapper--m-typeahead--PaddingTop))}.pf-c-select__toggle-wrapper .pf-c-chip-group{margin-top:var(--pf-c-select__toggle-wrapper--c-chip-group--MarginTop);margin-bottom:var(--pf-c-select__toggle-wrapper--c-chip-group--MarginBottom)}.pf-c-select__toggle-wrapper>.pf-c-select__toggle-typeahead:first-child{margin-left:calc(-1*var(--pf-c-select__toggle--PaddingLeft))}.pf-c-select__toggle-icon+.pf-c-select__toggle-text{margin-left:var(--pf-c-select__toggle-icon--toggle-text--MarginLeft)}.pf-c-select__toggle-badge{display:flex;padding-left:var(--pf-c-select__toggle-badge--PaddingLeft)}.pf-c-select__toggle-typeahead{flex-basis:var(--pf-c-select__toggle-typeahead--FlexBasis);flex-grow:1;min-width:var(--pf-c-select__toggle-typeahead--MinWidth);background-color:var(--pf-c-select__toggle-typeahead--BackgroundColor);border-top:var(--pf-c-select__toggle-typeahead--BorderTop);border-right:var(--pf-c-select__toggle-typeahead--BorderRight);border-bottom:inherit;border-left:var(--pf-c-select__toggle-typeahead--BorderLeft);flex-shrink:0}.pf-c-select__toggle-typeahead:focus{padding-bottom:var(--pf-c-select__toggle-typeahead--focus--PaddingBottom)}.pf-c-select__menu{position:absolute;top:var(--pf-c-select__menu--Top);z-index:var(--pf-c-select__menu--ZIndex);min-width:100%;padding-top:var(--pf-c-select__menu--PaddingTop);padding-bottom:var(--pf-c-select__menu--PaddingBottom);background-color:var(--pf-c-select__menu--BackgroundColor);background-clip:padding-box;box-shadow:var(--pf-c-select__menu--BoxShadow)}.pf-c-select__menu.pf-m-align-right{right:0}.pf-c-select.pf-m-top .pf-c-select__menu{top:0;transform:translateY(var(--pf-c-select__menu--m-top--TranslateY))}.pf-c-select__menu-fieldset{border:0}.pf-c-select__menu-wrapper{display:flex}.pf-c-select__menu-wrapper.pf-m-favorite .pf-c-select__menu-item.pf-m-favorite-action{--pf-c-select__menu-item--Color:var(--pf-c-select__menu-wrapper--m-favorite__menu-item--m-favorite-action--Color)}.pf-c-select__menu-item{position:relative;width:var(--pf-c-select__menu-item--Width);padding:var(--pf-c-select__menu-item--PaddingTop) var(--pf-c-select__menu-item--PaddingRight) var(--pf-c-select__menu-item--PaddingBottom) var(--pf-c-select__menu-item--PaddingLeft);font-size:var(--pf-c-select__menu-item--FontSize);font-weight:var(--pf-c-select__menu-item--FontWeight);line-height:var(--pf-c-select__menu-item--LineHeight);color:var(--pf-c-select__menu-item--Color);text-align:left;white-space:nowrap;background-color:transparent;border:none}.pf-c-select__menu-item.pf-m-focus,.pf-c-select__menu-item:focus,.pf-c-select__menu-item:hover{--pf-c-select__menu-item--m-action--Color:var(--pf-c-select__menu-item--hover__menu-item--m-action--Color);text-decoration:none}.pf-c-select__menu-item:hover,.pf-c-select__menu-wrapper:hover{background-color:var(--pf-c-select__menu-item--hover--BackgroundColor)}.pf-c-select__menu-item.pf-m-focus,.pf-c-select__menu-item:focus,.pf-c-select__menu-wrapper.pf-m-focus,.pf-c-select__menu-wrapper:focus-within{position:relative;background-color:var(--pf-c-select__menu-item--focus--BackgroundColor)}.pf-c-select__menu-item.pf-m-link{--pf-c-select__menu-item--PaddingRight:0;--pf-c-select__menu-item-main--PaddingRight:0;--pf-c-select__menu-item-description--PaddingRight:0;--pf-c-select__menu-item--Width:var(--pf-c-select__menu-item--m-link--Width);--pf-c-select__menu-item--hover--BackgroundColor:var(--pf-c-select__menu-item--m-link--hover--BackgroundColor);--pf-c-select__menu-item--focus--BackgroundColor:var(--pf-c-select__menu-item--m-link--focus--BackgroundColor);flex-grow:1}.pf-c-select__menu-item.pf-m-action{--pf-c-select__menu-item--Color:var(--pf-c-select__menu-item--m-action--Color);--pf-c-select__menu-item--Width:var(--pf-c-select__menu-item--m-action--Width);--pf-c-select__menu-item--hover--BackgroundColor:var(--pf-c-select__menu-item--m-action--hover--BackgroundColor);--pf-c-select__menu-item--focus--BackgroundColor:var(--pf-c-select__menu-item--m-action--focus--BackgroundColor);display:flex;align-items:flex-start;font-size:var(--pf-c-select__menu-item--m-action--FontSize)}.pf-c-select__menu-item.pf-m-action:hover{--pf-c-select__menu-item--m-action--Color:var(--pf-c-select__menu-item--m-action--hover--Color)}.pf-c-select__menu-item.pf-m-action:focus{--pf-c-select__menu-item--m-action--Color:var(--pf-c-select__menu-item--m-action--focus--Color)}.pf-c-select__menu-item.pf-m-selected{--pf-c-select__menu-item--PaddingRight:var(--pf-c-select__menu-item--m-selected--PaddingRight);--pf-c-select__menu-item-main--PaddingRight:var(--pf-c-select__menu-item--m-selected__menu-item-main--PaddingRight)}.pf-c-select__menu-item.pf-m-description{white-space:normal}.pf-c-select__menu-item.pf-m-description:not(.pf-c-check){--pf-c-select__menu-item--PaddingRight:0}.pf-c-select__menu-item.pf-m-description .pf-c-check__label{white-space:nowrap}.pf-c-select__menu-item.pf-m-disabled,.pf-c-select__menu-item:disabled,.pf-c-select__menu-wrapper.pf-m-disabled{color:var(--pf-c-select__menu-item--disabled--Color);pointer-events:none;background-color:var(--pf-c-select__menu-item--disabled--BackgroundColor)}.pf-c-select__menu-item-main{position:relative;display:block;padding-right:var(--pf-c-select__menu-item-main--PaddingRight);white-space:nowrap}.pf-c-select__menu-item-description{display:block;padding-right:var(--pf-c-select__menu-item-description--PaddingRight);font-size:var(--pf-c-select__menu-item-description--FontSize);color:var(--pf-c-select__menu-item-description--Color)}.pf-c-select__menu-item-icon{position:absolute;top:var(--pf-c-select__menu-item-icon--Top);right:var(--pf-c-select__menu-item-icon--Right);font-size:var(--pf-c-select__menu-item-icon--FontSize);color:var(--pf-c-select__menu-item-icon--Color);transform:translateY(var(--pf-c-select__menu-item-icon--TranslateY))}.pf-c-select__menu-item-action-icon{display:flex;align-items:center;min-height:var(--pf-c-select__menu-item-action-icon--MinHeight)}.pf-c-select__menu-item--match{font-weight:var(--pf-c-select__menu-item--match--FontWeight);background-color:inherit}.pf-c-select__menu-group+.pf-c-select__menu-group{padding-top:var(--pf-c-select__menu-group--menu-group--PaddingTop)}.pf-c-select__menu-search{padding:var(--pf-c-select__menu-search--PaddingTop) var(--pf-c-select__menu-search--PaddingRight) var(--pf-c-select__menu-search--PaddingBottom) var(--pf-c-select__menu-search--PaddingLeft)}.pf-c-select__menu-group-title{padding:var(--pf-c-select__menu-group-title--PaddingTop) var(--pf-c-select__menu-group-title--PaddingRight) var(--pf-c-select__menu-group-title--PaddingBottom) var(--pf-c-select__menu-group-title--PaddingLeft);font-size:var(--pf-c-select__menu-group-title--FontSize);font-weight:var(--pf-c-select__menu-group-title--FontWeight);color:var(--pf-c-select__menu-group-title--Color)}.pf-c-simple-list{--pf-c-simple-list__item-link--PaddingTop:var(--pf-global--spacer--xs);--pf-c-simple-list__item-link--PaddingRight:var(--pf-global--spacer--md);--pf-c-simple-list__item-link--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-simple-list__item-link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-simple-list__item-link--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-simple-list__item-link--Color:var(--pf-global--Color--100);--pf-c-simple-list__item-link--FontSize:var(--pf-global--FontSize--sm);--pf-c-simple-list__item-link--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-simple-list__item-link--m-current--Color:var(--pf-global--link--Color);--pf-c-simple-list__item-link--m-current--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-simple-list__item-link--m-current--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-simple-list__item-link--hover--Color:var(--pf-global--link--Color);--pf-c-simple-list__item-link--hover--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-simple-list__item-link--focus--Color:var(--pf-global--link--Color);--pf-c-simple-list__item-link--focus--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-simple-list__item-link--focus--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-simple-list__item-link--active--Color:var(--pf-global--link--Color);--pf-c-simple-list__item-link--active--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-simple-list__item-link--active--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-simple-list__title--PaddingTop:var(--pf-global--spacer--sm);--pf-c-simple-list__title--PaddingRight:var(--pf-global--spacer--md);--pf-c-simple-list__title--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-simple-list__title--PaddingLeft:var(--pf-global--spacer--md);--pf-c-simple-list__title--FontSize:var(--pf-global--FontSize--sm);--pf-c-simple-list__title--Color:var(--pf-global--Color--dark-200);--pf-c-simple-list__title--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-simple-list__section--section--MarginTop:var(--pf-global--spacer--sm)}.pf-c-simple-list__item-link{display:block;width:100%;padding:var(--pf-c-simple-list__item-link--PaddingTop) var(--pf-c-simple-list__item-link--PaddingRight) var(--pf-c-simple-list__item-link--PaddingBottom) var(--pf-c-simple-list__item-link--PaddingLeft);font-size:var(--pf-c-simple-list__item-link--FontSize);font-weight:var(--pf-c-simple-list__item-link--FontWeight);color:var(--pf-c-simple-list__item-link--Color);text-align:left;background-color:var(--pf-c-simple-list__item-link--BackgroundColor);border:none}.pf-c-simple-list__item-link.pf-m-current{--pf-c-simple-list__item-link--FontWeight:var(--pf-c-simple-list__item-link--m-current--FontWeight);--pf-c-simple-list__item-link--BackgroundColor:var(--pf-c-simple-list__item-link--m-current--BackgroundColor);--pf-c-simple-list__item-link--Color:var(--pf-c-simple-list__item-link--m-current--Color)}.pf-c-simple-list__item-link:hover{text-decoration:none;--pf-c-simple-list__item-link--BackgroundColor:var(--pf-c-simple-list__item-link--hover--BackgroundColor);--pf-c-simple-list__item-link--Color:var(--pf-c-simple-list__item-link--hover--Color)}.pf-c-simple-list__item-link:focus{--pf-c-simple-list__item-link--FontWeight:var(--pf-c-simple-list__item-link--focus--FontWeight);--pf-c-simple-list__item-link--BackgroundColor:var(--pf-c-simple-list__item-link--focus--BackgroundColor);--pf-c-simple-list__item-link--Color:var(--pf-c-simple-list__item-link--focus--Color)}.pf-c-simple-list__item-link:active{--pf-c-simple-list__item-link--FontWeight:var(--pf-c-simple-list__item-link--active--FontWeight);--pf-c-simple-list__item-link--BackgroundColor:var(--pf-c-simple-list__item-link--active--BackgroundColor);--pf-c-simple-list__item-link--Color:var(--pf-c-simple-list__item-link--active--Color)}.pf-c-simple-list__title{padding:var(--pf-c-simple-list__title--PaddingTop) var(--pf-c-simple-list__title--PaddingRight) var(--pf-c-simple-list__title--PaddingBottom) var(--pf-c-simple-list__title--PaddingLeft);font-size:var(--pf-c-simple-list__title--FontSize);font-weight:var(--pf-c-simple-list__title--FontWeight);color:var(--pf-c-simple-list__title--Color)}.pf-c-simple-list__section+.pf-c-simple-list__section{margin-top:var(--pf-c-simple-list__section--section--MarginTop)}.pf-c-skeleton{--pf-c-skeleton--BackgroundColor:var(--pf-global--palette--black-150);--pf-c-skeleton--Width:auto;--pf-c-skeleton--Height:auto;--pf-c-skeleton--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-skeleton--before--PaddingBottom:0;--pf-c-skeleton--before--Height:auto;--pf-c-skeleton--before--Content:"\00a0";--pf-c-skeleton--after--LinearGradientAngle:90deg;--pf-c-skeleton--after--LinearGradientColorStop1:hsla(0,0%,92.9%,0);--pf-c-skeleton--after--LinearGradientColorStop2:#ededed;--pf-c-skeleton--after--LinearGradientColorStop3:hsla(0,0%,92.9%,0);--pf-c-skeleton--after--TranslateX:-100%;--pf-c-skeleton--after--AnimationName:pf-c-skeleton-loading;--pf-c-skeleton--after--AnimationDuration:2s;--pf-c-skeleton--after--AnimationIterationCount:infinite;--pf-c-skeleton--after--AnimationTimingFunction:linear;--pf-c-skeleton--after--AnimationDelay:.5s;--pf-c-skeleton--m-circle--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-skeleton--m-circle--before--PaddingBottom:100%;--pf-c-skeleton--m-text-4xl--Height:calc(var(--pf-global--FontSize--4xl)*var(--pf-global--LineHeight--sm));--pf-c-skeleton--m-text-3xl--Height:calc(var(--pf-global--FontSize--3xl)*var(--pf-global--LineHeight--sm));--pf-c-skeleton--m-text-2xl--Height:calc(var(--pf-global--FontSize--2xl)*var(--pf-global--LineHeight--sm));--pf-c-skeleton--m-text-xl--Height:calc(var(--pf-global--FontSize--xl)*var(--pf-global--LineHeight--sm));--pf-c-skeleton--m-text-lg--Height:calc(var(--pf-global--FontSize--lg)*var(--pf-global--LineHeight--md));--pf-c-skeleton--m-text-md--Height:calc(var(--pf-global--FontSize--md)*var(--pf-global--LineHeight--md));--pf-c-skeleton--m-text-sm--Height:calc(var(--pf-global--FontSize--sm)*var(--pf-global--LineHeight--md));--pf-c-skeleton--m-width-sm--Width:6.25rem;--pf-c-skeleton--m-width-md--Width:12.5rem;--pf-c-skeleton--m-width-lg--Width:18.75rem;--pf-c-skeleton--m-width-25--Width:25%;--pf-c-skeleton--m-width-33--Width:33.33333%;--pf-c-skeleton--m-width-50--Width:50%;--pf-c-skeleton--m-width-66--Width:66.66667%;--pf-c-skeleton--m-width-75--Width:75%;--pf-c-skeleton--m-height-sm--Height:6.25rem;--pf-c-skeleton--m-height-md--Height:12.5rem;--pf-c-skeleton--m-height-lg--Height:18.75rem;--pf-c-skeleton--m-height-25--Height:25%;--pf-c-skeleton--m-height-33--Height:33.33333%;--pf-c-skeleton--m-height-50--Height:50%;--pf-c-skeleton--m-height-66--Height:66.66667%;--pf-c-skeleton--m-height-75--Height:75%;--pf-c-skeleton--m-height-100--Height:100%;position:relative;width:var(--pf-c-skeleton--Width);height:var(--pf-c-skeleton--Height);overflow:hidden;background-color:var(--pf-c-skeleton--BackgroundColor);border-radius:var(--pf-c-skeleton--BorderRadius);transform:translate(0)}.pf-c-skeleton:before{display:block;height:var(--pf-c-skeleton--before--Height);padding-bottom:var(--pf-c-skeleton--before--PaddingBottom);content:var(--pf-c-skeleton--before--Content)}.pf-c-skeleton:after{position:absolute;top:0;right:0;bottom:0;left:0;display:block;content:"";background:linear-gradient(var(--pf-c-skeleton--after--LinearGradientAngle),var(--pf-c-skeleton--after--LinearGradientColorStop1),var(--pf-c-skeleton--after--LinearGradientColorStop2),var(--pf-c-skeleton--after--LinearGradientColorStop3));transform:translateX(var(--pf-c-skeleton--after--TranslateX));animation:var(--pf-c-skeleton--after--AnimationName) var(--pf-c-skeleton--after--AnimationDuration) var(--pf-c-skeleton--after--AnimationTimingFunction) var(--pf-c-skeleton--after--AnimationDelay) var(--pf-c-skeleton--after--AnimationIterationCount)}.pf-c-skeleton.pf-m-circle{--pf-c-skeleton--BorderRadius:var(--pf-c-skeleton--m-circle--BorderRadius)}.pf-c-skeleton.pf-m-circle,.pf-c-skeleton.pf-m-square{--pf-c-skeleton--before--Height:0;--pf-c-skeleton--before--PaddingBottom:var(--pf-c-skeleton--m-circle--before--PaddingBottom)}.pf-c-skeleton.pf-m-width-sm{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-sm--Width)}.pf-c-skeleton.pf-m-width-md{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-md--Width)}.pf-c-skeleton.pf-m-width-lg{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-lg--Width)}.pf-c-skeleton.pf-m-width-25{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-25--Width)}.pf-c-skeleton.pf-m-width-33{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-33--Width)}.pf-c-skeleton.pf-m-width-50{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-50--Width)}.pf-c-skeleton.pf-m-width-66{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-66--Width)}.pf-c-skeleton.pf-m-width-75{--pf-c-skeleton--Width:var(--pf-c-skeleton--m-width-75--Width)}.pf-c-skeleton.pf-m-height-sm{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-sm--Height)}.pf-c-skeleton.pf-m-height-md{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-md--Height)}.pf-c-skeleton.pf-m-height-lg{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-lg--Height)}.pf-c-skeleton.pf-m-height-25{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-25--Height)}.pf-c-skeleton.pf-m-height-33{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-33--Height)}.pf-c-skeleton.pf-m-height-50{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-50--Height)}.pf-c-skeleton.pf-m-height-66{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-66--Height)}.pf-c-skeleton.pf-m-height-75{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-75--Height)}.pf-c-skeleton.pf-m-height-100{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-height-100--Height)}.pf-c-skeleton.pf-m-text-4xl{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-4xl--Height)}.pf-c-skeleton.pf-m-text-3xl{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-3xl--Height)}.pf-c-skeleton.pf-m-text-2xl{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-2xl--Height)}.pf-c-skeleton.pf-m-text-xl{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-xl--Height)}.pf-c-skeleton.pf-m-text-lg{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-lg--Height)}.pf-c-skeleton.pf-m-text-md{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-md--Height)}.pf-c-skeleton.pf-m-text-sm{--pf-c-skeleton--Height:var(--pf-c-skeleton--m-text-sm--Height)}@keyframes pf-c-skeleton-loading{0%{transform:translateX(-100%)}60%{transform:translateX(100%)}to{transform:translateX(100%)}}.pf-c-skip-to-content{--pf-c-skip-to-content--Top:var(--pf-global--spacer--md);--pf-c-skip-to-content--ZIndex:var(--pf-global--ZIndex--2xl);--pf-c-skip-to-content--focus--Left:var(--pf-global--spacer--md);position:absolute;top:var(--pf-c-skip-to-content--Top);left:-300%;z-index:var(--pf-c-skip-to-content--ZIndex)}.pf-c-skip-to-content:focus{left:var(--pf-c-skip-to-content--focus--Left)}.pf-c-slider{--pf-c-slider__rail--PaddingTop:var(--pf-global--spacer--md);--pf-c-slider__rail--PaddingBottom:var(--pf-global--spacer--md);--pf-c-slider__rail-track--Height:0.25rem;--pf-c-slider__rail-track--before--base--BackgroundColor:var(--pf-global--BorderColor--100);--pf-c-slider__rail-track--before--fill--BackgroundColor:var(--pf-global--active-color--300);--pf-c-slider__rail-track--before--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-slider__rail-track--before--fill--BackgroundColor--gradient-stop:var(--pf-c-slider--value);--pf-c-slider__steps--FontSize:var(--pf-global--FontSize--sm);--pf-c-slider__steps--Height:var(--pf-c-slider__steps--FontSize);--pf-c-slider__step-tick--Top:var(--pf-global--spacer--md);--pf-c-slider__step-tick--Width:0.25rem;--pf-c-slider__step-tick--Height:0.25rem;--pf-c-slider__step-tick--BackgroundColor:var(--pf-global--BorderColor--200);--pf-c-slider__step-tick--TranslateX:-50%;--pf-c-slider__step-tick--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-slider__step--m-active__slider-tick--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-slider__step--first-child__step-tick--TranslateX:0;--pf-c-slider__step--last-child__step-tick--TranslateX:-100%;--pf-c-slider__step-label--TranslateX:-50%;--pf-c-slider__step-label--Top:calc(var(--pf-global--spacer--xl) + var(--pf-c-slider__rail-track--Height));--pf-c-slider__step--first-child__step-label--TranslateX:0;--pf-c-slider__step--last-child__step-label--TranslateX:-100%;--pf-c-slider__thumb--Top:calc(var(--pf-c-slider__rail-track--Height)/2 + var(--pf-global--spacer--md));--pf-c-slider__thumb--Width:1rem;--pf-c-slider__thumb--Height:1rem;--pf-c-slider__thumb--Left:var(--pf-c-slider--value);--pf-c-slider__thumb--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-slider__thumb--TranslateX:-50%;--pf-c-slider__thumb--TranslateY:-50%;--pf-c-slider__thumb--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-slider__thumb--BoxShadow--base:0 0 0 2px var(--pf-global--BackgroundColor--100),0 0 0 3px var(--pf-global--primary-color--100);--pf-c-slider__thumb--hover--BoxShadow:var(--pf-c-slider__thumb--BoxShadow--base);--pf-c-slider__thumb--focus--BoxShadow:var(--pf-c-slider__thumb--BoxShadow--base);--pf-c-slider__thumb--active--BoxShadow:var(--pf-c-slider__thumb--BoxShadow--base),0 0 2px 5px var(--pf-global--active-color--200);--pf-c-slider__value--MarginLeft:var(--pf-global--spacer--md);--pf-c-slider__value--c-form-control--width-base:3.5ch;--pf-c-slider__value--c-form-control--width-chars:3;--pf-c-slider__value--c-form-control--Width:calc(var(--pf-c-slider__value--c-form-control--width-base) + var(--pf-c-slider__value--c-form-control--width-chars)*1ch);--pf-c-slider__value--m-floating--TranslateX:-50%;--pf-c-slider__value--m-floating--TranslateY:-100%;--pf-c-slider__value--m-floating--Left:var(--pf-c-slider--value);--pf-c-slider__value--m-floating--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-slider__actions--MarginRight:var(--pf-global--spacer--sm);--pf-c-slider__main--actions--MarginLeft:var(--pf-global--spacer--sm);display:flex}.pf-c-slider__main{position:relative;flex-grow:1}.pf-c-slider__rail{padding-top:var(--pf-c-slider__rail--PaddingTop);padding-bottom:var(--pf-c-slider__rail--PaddingBottom)}.pf-c-slider__rail-track{position:relative;height:var(--pf-c-slider__rail-track--Height)}.pf-c-slider__rail-track:before{position:absolute;top:0;right:0;bottom:0;left:0;content:"";background:linear-gradient(90deg,var(--pf-c-slider__rail-track--before--fill--BackgroundColor),var(--pf-c-slider__rail-track--before--fill--BackgroundColor) var(--pf-c-slider__rail-track--before--fill--BackgroundColor--gradient-stop),var(--pf-c-slider__rail-track--before--base--BackgroundColor) var(--pf-c-slider__rail-track--before--fill--BackgroundColor--gradient-stop));border-radius:var(--pf-c-slider__rail-track--before--BorderRadius)}.pf-c-slider__steps{height:var(--pf-c-slider__steps--Height);font-size:var(--pf-c-slider__steps--FontSize);line-height:1}.pf-c-slider__step{position:absolute;top:0;left:var(--pf-c-slider__step--Left);content:""}.pf-c-slider__step.pf-m-active{--pf-c-slider__step-tick--BackgroundColor:var(--pf-c-slider__step--m-active__slider-tick--BackgroundColor)}.pf-c-slider__step:first-child{--pf-c-slider__step-tick--TranslateX:var(--pf-c-slider__step--first-child__step-tick--TranslateX);--pf-c-slider__step-label--TranslateX:var(--pf-c-slider__step--first-child__step-label--TranslateX)}.pf-c-slider__step:last-child{--pf-c-slider__step-tick--TranslateX:var(--pf-c-slider__step--last-child__step-tick--TranslateX);--pf-c-slider__step-label--TranslateX:var(--pf-c-slider__step--last-child__step-label--TranslateX)}.pf-c-slider__step-tick{position:absolute;top:var(--pf-c-slider__step-tick--Top);left:0;width:var(--pf-c-slider__step-tick--Width);height:var(--pf-c-slider__step-tick--Height);background-color:var(--pf-c-slider__step-tick--BackgroundColor);border-radius:var(--pf-c-slider__step-tick--BorderRadius);transform:translateX(var(--pf-c-slider__step-tick--TranslateX))}.pf-c-slider__step-label{position:absolute;top:var(--pf-c-slider__step-label--Top);transform:translateX(var(--pf-c-slider__step-label--TranslateX))}.pf-c-slider__thumb{position:absolute;top:var(--pf-c-slider__thumb--Top);left:var(--pf-c-slider__thumb--Left);width:var(--pf-c-slider__thumb--Width);height:var(--pf-c-slider__thumb--Height);cursor:pointer;background-color:var(--pf-c-slider__thumb--BackgroundColor);border-radius:var(--pf-c-slider__thumb--BorderRadius);box-shadow:var(--pf-c-slider__thumb--BoxShadow);transform:translate(var(--pf-c-slider__thumb--TranslateX),var(--pf-c-slider__thumb--TranslateY))}.pf-c-slider__thumb:hover{--pf-c-slider__thumb--BoxShadow:var(--pf-c-slider__thumb--hover--BoxShadow)}.pf-c-slider__thumb:focus{--pf-c-slider__thumb--BoxShadow:var(--pf-c-slider__thumb--focus--BoxShadow);outline:0}.pf-c-slider__thumb:active{--pf-c-slider__thumb--BoxShadow:var(--pf-c-slider__thumb--active--BoxShadow)}.pf-c-slider__value{margin-left:var(--pf-c-slider__value--MarginLeft)}.pf-c-slider__value.pf-m-floating{--pf-c-slider__value--MarginLeft:0;position:absolute;top:0;left:var(--pf-c-slider__value--m-floating--Left);z-index:var(--pf-c-slider__value--m-floating--ZIndex);transform:translate(var(--pf-c-slider__value--m-floating--TranslateX),var(--pf-c-slider__value--m-floating--TranslateY))}.pf-c-slider__value.pf-m-floating .pf-c-input-group{align-items:center}.pf-c-slider__value.pf-m-floating .pf-c-input-group__text{position:absolute;left:100%}.pf-c-slider__value .pf-c-form-control{width:var(--pf-c-slider__value--c-form-control--Width)}.pf-c-slider__actions{display:flex;margin-right:var(--pf-c-slider__actions--MarginRight)}.pf-c-slider__main~.pf-c-slider__actions{--pf-c-slider__actions--MarginRight:0;margin-left:var(--pf-c-slider__main--actions--MarginLeft)}.pf-c-spinner{--pf-c-spinner--AnimationDuration:1.5s;--pf-c-spinner--AnimationTimingFunction:cubic-bezier(.77,.005,.315,1);--pf-c-spinner--diameter:var(--pf-global--icon--FontSize--xl);--pf-c-spinner--stroke-width-multiplier:.1;--pf-c-spinner--stroke-width:calc(var(--pf-c-spinner--diameter)*var(--pf-c-spinner--stroke-width-multiplier));--pf-c-spinner--Width:var(--pf-c-spinner--diameter);--pf-c-spinner--Height:var(--pf-c-spinner--diameter);--pf-c-spinner--Color:var(--pf-global--primary-color--100);--pf-c-spinner--m-sm--diameter:var(--pf-global--icon--FontSize--sm);--pf-c-spinner--m-md--diameter:var(--pf-global--icon--FontSize--md);--pf-c-spinner--m-lg--diameter:var(--pf-global--icon--FontSize--lg);--pf-c-spinner--m-xl--diameter:var(--pf-global--icon--FontSize--xl);--pf-c-spinner__clipper--Width:var(--pf-c-spinner--diameter);--pf-c-spinner__clipper--Height:var(--pf-c-spinner--diameter);--pf-c-spinner__clipper--after--BoxShadowColor:var(--pf-c-spinner--Color);--pf-c-spinner__clipper--after--Width:var(--pf-c-spinner--diameter);--pf-c-spinner__clipper--after--Height:var(--pf-c-spinner--diameter);--pf-c-spinner__clipper--after--BoxShadowSpreadRadius:var(--pf-c-spinner--stroke-width);--pf-c-spinner__lead-ball--after--BackgroundColor:var(--pf-c-spinner--Color);--pf-c-spinner__ball--after--Width:var(--pf-c-spinner--stroke-width);--pf-c-spinner__ball--after--Height:var(--pf-c-spinner--stroke-width);--pf-c-spinner__tail-ball--after--BackgroundColor:var(--pf-c-spinner--Color);position:relative;display:inline-block;width:var(--pf-c-spinner--Width);height:var(--pf-c-spinner--Height);text-align:left;animation:pf-animation-spinner-parent calc(var(--pf-c-spinner--AnimationDuration)*2) var(--pf-c-spinner--AnimationTimingFunction) infinite}.pf-c-spinner.pf-m-sm{--pf-c-spinner--diameter:var(--pf-c-spinner--m-sm--diameter)}.pf-c-spinner.pf-m-md{--pf-c-spinner--diameter:var(--pf-c-spinner--m-md--diameter)}.pf-c-spinner.pf-m-lg{--pf-c-spinner--diameter:var(--pf-c-spinner--m-lg--diameter)}.pf-c-spinner.pf-m-xl{--pf-c-spinner--diameter:var(--pf-c-spinner--m-xl--diameter)}@keyframes pf-animation-spinner-parent{0%{transform:rotate(0deg)}50%{transform:rotate(-540deg)}to{transform:rotate(-3turn)}}.pf-c-spinner__clipper{position:absolute;width:var(--pf-c-spinner__clipper--Width);height:var(--pf-c-spinner__clipper--Height);clip-path:inset(0 0 50% 50%);animation:pf-animation-spinner__clipper var(--pf-c-spinner--AnimationDuration) linear infinite}@keyframes pf-animation-spinner__clipper{0%{transform:rotate(0deg)}to{transform:rotate(-270deg)}}.pf-c-spinner__clipper:after{position:absolute;width:var(--pf-c-spinner__clipper--after--Width);height:var(--pf-c-spinner__clipper--after--Height);clip-path:inset(0 0 0 50%);content:"";border-radius:50%;box-shadow:inset 0 0 0 var(--pf-c-spinner__clipper--after--BoxShadowSpreadRadius) var(--pf-c-spinner__clipper--after--BoxShadowColor);animation:pf-animation-spinner__clipper-after var(--pf-c-spinner--AnimationDuration) linear infinite}@keyframes pf-animation-spinner__clipper-after{0%{transform:rotate(90deg)}to{transform:rotate(-180deg)}}.pf-c-spinner__lead-ball{position:absolute;top:0;left:0;width:100%;height:100%;animation:pf-animation-spinner__lead-ball var(--pf-c-spinner--AnimationDuration) linear infinite}.pf-c-spinner__lead-ball:after{position:absolute;top:calc(50% - var(--pf-c-spinner__ball--after--Height)/2);right:0;width:var(--pf-c-spinner__ball--after--Width);height:var(--pf-c-spinner__ball--after--Height);content:"";background-color:var(--pf-c-spinner__lead-ball--after--BackgroundColor);border-radius:50%;transform-origin:top right}@keyframes pf-animation-spinner__lead-ball{0%{transform:rotate(0deg)}34%{transform:rotate(-180deg)}to{transform:rotate(-1turn)}}.pf-c-spinner__tail-ball{position:absolute;top:0;left:0;width:100%;height:100%;animation:pf-animation-spinner__tail-ball var(--pf-c-spinner--AnimationDuration) linear infinite}.pf-c-spinner__tail-ball:after{position:absolute;top:calc(50% - var(--pf-c-spinner__ball--after--Height)/2);right:0;width:var(--pf-c-spinner__ball--after--Width);height:var(--pf-c-spinner__ball--after--Height);content:"";background-color:var(--pf-c-spinner__tail-ball--after--BackgroundColor);border-radius:50%;transform-origin:top right}@keyframes pf-animation-spinner__tail-ball{0%{transform:rotate(0deg)}67.5%{transform:rotate(-180deg)}to{transform:rotate(-1turn)}}.pf-c-switch{--pf-c-switch--FontSize:var(--pf-global--FontSize--md);--pf-c-switch__toggle-icon--FontSize:calc(var(--pf-c-switch--FontSize)*0.625);--pf-c-switch__toggle-icon--Color:var(--pf-global--Color--light-100);--pf-c-switch__toggle-icon--Left:calc(var(--pf-c-switch--FontSize)*0.4);--pf-c-switch__toggle-icon--Offset:0.125rem;--pf-c-switch--LineHeight:var(--pf-global--LineHeight--md);--pf-c-switch--Height:calc(var(--pf-c-switch--FontSize)*var(--pf-c-switch--LineHeight));--pf-c-switch__input--checked__toggle--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-switch__input--checked__toggle--before--TranslateX:calc(100% + var(--pf-c-switch__toggle-icon--Offset));--pf-c-switch__input--checked__label--Color:var(--pf-global--Color--dark-100);--pf-c-switch__input--not-checked__label--Color:var(--pf-global--disabled-color--100);--pf-c-switch__input--disabled__label--Color:var(--pf-global--disabled-color--100);--pf-c-switch__input--disabled__toggle--BackgroundColor:var(--pf-global--Color--dark-200);--pf-c-switch__input--disabled__toggle--before--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-switch__input--focus__toggle--OutlineWidth:var(--pf-global--BorderWidth--md);--pf-c-switch__input--focus__toggle--OutlineOffset:var(--pf-global--spacer--sm);--pf-c-switch__input--focus__toggle--OutlineColor:var(--pf-global--primary-color--100);--pf-c-switch__toggle--Height:calc(var(--pf-c-switch--FontSize)*var(--pf-c-switch--LineHeight));--pf-c-switch__toggle--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-switch__toggle--BorderRadius:var(--pf-c-switch--Height);--pf-c-switch__toggle--before--Width:calc(var(--pf-c-switch--FontSize) - var(--pf-c-switch__toggle-icon--Offset));--pf-c-switch__toggle--before--Height:var(--pf-c-switch__toggle--before--Width);--pf-c-switch__toggle--before--Top:calc((var(--pf-c-switch--Height) - var(--pf-c-switch__toggle--before--Height))/2);--pf-c-switch__toggle--before--Left:var(--pf-c-switch__toggle--before--Top);--pf-c-switch__toggle--before--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-switch__toggle--before--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-switch__toggle--before--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-switch__toggle--before--Transition:transform .25s ease 0s;--pf-c-switch__toggle--Width:calc(var(--pf-c-switch--Height) + var(--pf-c-switch__toggle-icon--Offset) + var(--pf-c-switch__toggle--before--Width));--pf-c-switch__label--PaddingLeft:var(--pf-global--spacer--md);--pf-c-switch__label--Color:var(--pf-global--Color--dark-100);position:relative;display:inline-block;height:var(--pf-c-switch--Height);font-size:var(--pf-c-switch--FontSize);line-height:var(--pf-c-switch--LineHeight);vertical-align:middle;cursor:pointer}.pf-c-switch__input{position:absolute;cursor:pointer;opacity:0}.pf-c-switch__input:focus~.pf-c-switch__toggle{outline:var(--pf-c-switch__input--focus__toggle--OutlineWidth) solid var(--pf-c-switch__input--focus__toggle--OutlineColor);outline-offset:var(--pf-c-switch__input--focus__toggle--OutlineOffset)}.pf-c-switch__input:checked~.pf-c-switch__label{color:var(--pf-c-switch__input--checked__label--Color)}.pf-c-switch__input:checked~.pf-c-switch__toggle{background-color:var(--pf-c-switch__input--checked__toggle--BackgroundColor)}.pf-c-switch__input:checked~.pf-c-switch__toggle:before{transform:translateX(var(--pf-c-switch__input--checked__toggle--before--TranslateX))}.pf-c-switch__input:checked~.pf-m-off{display:none}.pf-c-switch__input:not(:checked)~.pf-c-switch__label{color:var(--pf-c-switch__input--not-checked__label--Color)}.pf-c-switch__input:not(:checked)~.pf-c-switch__toggle .pf-c-switch__toggle-icon{display:none;visibility:hidden}.pf-c-switch__input:not(:checked)~.pf-m-on{display:none}.pf-c-switch__input:disabled{cursor:not-allowed}.pf-c-switch__input:disabled~.pf-c-switch__label{color:var(--pf-c-switch__input--disabled__label--Color);cursor:not-allowed}.pf-c-switch__input:disabled~.pf-c-switch__toggle{cursor:not-allowed;background-color:var(--pf-c-switch__input--disabled__toggle--BackgroundColor)}.pf-c-switch__input:disabled~.pf-c-switch__toggle:before{background-color:var(--pf-c-switch__input--disabled__toggle--before--BackgroundColor)}.pf-c-switch__toggle{position:relative;display:inline-block;width:var(--pf-c-switch__toggle--Width);height:var(--pf-c-switch__toggle--Height);background-color:var(--pf-c-switch__toggle--BackgroundColor);border-radius:var(--pf-c-switch__toggle--BorderRadius)}.pf-c-switch__toggle:before{position:absolute;top:var(--pf-c-switch__toggle--before--Top);left:var(--pf-c-switch__toggle--before--Left);display:block;width:var(--pf-c-switch__toggle--before--Width);height:var(--pf-c-switch__toggle--before--Height);content:"";background-color:var(--pf-c-switch__toggle--before--BackgroundColor);border-radius:var(--pf-c-switch__toggle--before--BorderRadius);box-shadow:var(--pf-c-switch__toggle--before--BoxShadow);transition:var(--pf-c-switch__toggle--before--Transition)}.pf-c-switch__toggle-icon{position:absolute;top:0;bottom:0;left:var(--pf-c-switch__toggle-icon--Left);display:flex;align-items:center;font-size:var(--pf-c-switch__toggle-icon--FontSize);color:var(--pf-c-switch__toggle-icon--Color)}.pf-c-switch__label{display:inline-block;padding-left:var(--pf-c-switch__label--PaddingLeft);color:var(--pf-c-switch__label--Color);vertical-align:top}.pf-c-tab-content{--pf-c-tab-content--m-light-300:var(--pf-global--BackgroundColor--light-300)}.pf-c-tab-content.pf-m-light-300{background-color:var(--pf-c-tab-content--m-light-300)}.pf-c-table[class*=pf-m-grid]{--pf-c-table--responsive--BorderColor:var(--pf-global--BorderColor--300);--pf-c-table--tbody--responsive--border-width--base:var(--pf-global--spacer--sm);--pf-c-table--tbody--after--border-width--base:var(--pf-global--BorderWidth--lg);--pf-c-table--tbody--after--BorderLeftWidth:0;--pf-c-table--tbody--after--BorderColor:var(--pf-global--active-color--100);--pf-c-table-tr--responsive--border-width--base:var(--pf-global--spacer--sm);--pf-c-table-tr--responsive--last-child--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-table-tr--responsive--GridColumnGap:var(--pf-global--spacer--md);--pf-c-table-tr--responsive--MarginTop:var(--pf-global--spacer--sm);--pf-c-table-tr--responsive--PaddingTop:var(--pf-global--spacer--md);--pf-c-table-tr--responsive--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table-tr--responsive--xl--PaddingRight:var(--pf-global--spacer--md);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-global--spacer--md);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table-tr--responsive--xl--PaddingLeft:var(--pf-global--spacer--md);--pf-c-table-tr--responsive--nested-table--PaddingTop:var(--pf-global--spacer--xl);--pf-c-table-tr--responsive--nested-table--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table-tr--responsive--nested-table--PaddingBottom:var(--pf-global--spacer--xl);--pf-c-table-tr--responsive--nested-table--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table--m-grid--cell--hidden-visible--Display:grid;--pf-c-table--m-grid--cell--PaddingTop:0;--pf-c-table--m-grid--cell--PaddingRight:0;--pf-c-table--m-grid--cell--PaddingBottom:0;--pf-c-table--m-grid--cell--PaddingLeft:0;--pf-c-table-td--responsive--GridColumnGap:var(--pf-global--spacer--md);--pf-c-table--cell--responsive--PaddingTop:var(--pf-global--spacer--md);--pf-c-table--cell--responsive--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-table--cell--first-child--responsive--PaddingTop:var(--pf-global--spacer--sm);--pf-c-table--cell--responsive--PaddingRight:0;--pf-c-table--cell--responsive--PaddingLeft:0;--pf-c-table--m-compact-tr--responsive--PaddingTop:var(--pf-global--spacer--sm);--pf-c-table--m-compact-tr--responsive--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-table--m-compact-tr-td--responsive--PaddingTop:var(--pf-global--spacer--xs);--pf-c-table--m-compact-tr-td--responsive--PaddingBottom:var(--pf-global--spacer--xs);--pf-c-table--m-compact__action--responsive--MarginTop:calc(var(--pf-global--spacer--xs)*-1);--pf-c-table--m-compact__action--responsive--MarginBottom:calc(var(--pf-global--spacer--xs)*-1);--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom:-0.375rem;--pf-c-table__expandable-row-content--responsive--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table__expandable-row-content--responsive--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table__expandable-row-content--responsive--xl--PaddingRight:var(--pf-global--spacer--md);--pf-c-table__expandable-row-content--responsive--xl--PaddingLeft:var(--pf-global--spacer--md);--pf-c-table__expandable-row-content--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-table__check--responsive--MarginLeft:var(--pf-global--spacer--sm);--pf-c-table__check--responsive--MarginTop:0.875rem;--pf-c-table--m-grid__favorite--MarginTop:0.5rem;--pf-c-table--m-grid__check--favorite--MarginLeft:var(--pf-global--spacer--xl);--pf-c-table--m-grid__action--MarginTop:0.375rem;--pf-c-table__action--responsive--MarginLeft:var(--pf-global--spacer--xl);--pf-c-table--m-grid__favorite--action--MarginLeft:var(--pf-global--spacer--2xl);--pf-c-table--m-grid__check--favorite--action--MarginLeft:calc(var(--pf-c-table--m-grid__check--favorite--MarginLeft) + var(--pf-c-table--m-grid__favorite--action--MarginLeft));--pf-c-table__toggle__icon--Transition:.2s ease-in 0s;--pf-c-table__toggle--m-expanded__icon--Rotate:180deg}@media screen and (max-width:1200px){.pf-c-table[class*=pf-m-grid]{--pf-c-table-tr--responsive--PaddingRight:var(--pf-c-table-tr--responsive--xl--PaddingRight);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-c-table-tr--responsive--xl--PaddingLeft);--pf-c-table__expandable-row-content--responsive--PaddingRight:var(--pf-c-table__expandable-row-content--responsive--xl--PaddingRight);--pf-c-table__expandable-row-content--responsive--PaddingLeft:var(--pf-c-table__expandable-row-content--responsive--xl--PaddingLeft)}}.pf-m-grid.pf-c-table{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft);--pf-c-table__favorite--c-button--MarginTop:auto;--pf-c-table__favorite--c-button--MarginRight:auto;--pf-c-table__favorite--c-button--MarginBottom:auto;--pf-c-table__favorite--c-button--MarginLeft:auto;display:grid;border:none}.pf-m-grid.pf-c-table tr>*{width:auto;min-width:0;max-width:none;overflow:visible;text-overflow:clip;white-space:normal}.pf-m-grid.pf-c-table .pf-c-table__text{position:relative;width:auto;min-width:0;max-width:none;overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-m-grid.pf-c-table thead{display:none;visibility:hidden}.pf-m-grid.pf-c-table tbody{display:block}.pf-m-grid.pf-c-table tbody:first-of-type{border-top:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid.pf-c-table table.pf-m-compact>tbody{border-top:0}.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row){border-bottom:var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid.pf-c-table tbody:last-of-type:not(:only-of-type)>tr,.pf-m-grid.pf-c-table tr:last-child{border-bottom-width:var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth)}.pf-m-grid.pf-c-table tbody.pf-m-expanded{border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-m-grid.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row){border-bottom:0}.pf-m-grid.pf-c-table tbody.pf-m-expanded:not(:last-of-type){border-bottom:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row){display:grid;grid-template-columns:1fr;height:auto;grid-auto-columns:max-content;grid-column-gap:var(--pf-c-table-tr--responsive--GridColumnGap);padding:var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft)}.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row)>*{padding:var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft)}.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row)>:first-child{--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--cell--first-child--responsive--PaddingTop)}.pf-m-grid.pf-c-table.pf-m-compact{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table--m-compact-tr--responsive--PaddingTop);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);--pf-c-table--cell--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);--pf-c-table__check--input--MarginTop:0}.pf-m-grid.pf-c-table.pf-m-compact .pf-c-table__action{margin-top:var(--pf-c-table--m-compact__action--responsive--MarginTop);margin-bottom:var(--pf-c-table--m-compact__action--responsive--MarginTop)}.pf-m-grid.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button{margin-bottom:var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom)}.pf-m-grid.pf-c-table .pf-c-table__icon>*{text-align:left}.pf-m-grid.pf-c-table [data-label]{--pf-c-table--cell--hidden-visible--Display:var(--pf-c-table--m-grid--cell--hidden-visible--Display);grid-column:1;grid-column-gap:var(--pf-c-table-td--responsive--GridColumnGap);grid-template-columns:1fr minmax(0,1.5fr);align-items:start}.pf-m-grid.pf-c-table [data-label]>*{grid-column:2}.pf-m-grid.pf-c-table [data-label]:before{font-weight:700;text-align:left;content:attr(data-label)}.pf-m-grid.pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:0}.pf-m-grid.pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:0}.pf-m-grid.pf-c-table .pf-c-table{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table-tr--responsive--nested-table--PaddingTop);--pf-c-table-tr--responsive--PaddingRight:var(--pf-c-table-tr--responsive--nested-table--PaddingRight);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);border:0}.pf-m-grid.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row)+tr:not(.pf-c-table__expandable-row){--pf-c-table-tr--responsive--PaddingTop:0}.pf-m-grid.pf-c-table .pf-c-table__compound-expansion-toggle{--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:0;--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0;--pf-c-table__compound-expansion-toggle__button--after--Top:100%}.pf-m-grid.pf-c-table tbody{position:relative}.pf-m-grid.pf-c-table tbody:after{position:absolute;top:0;bottom:0;left:0;content:"";border:0;border-left:var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor)}.pf-m-grid.pf-c-table tbody.pf-m-expanded{--pf-c-table--tbody--after--BorderLeftWidth:var(--pf-c-table--tbody--after--border-width--base)}.pf-m-grid.pf-c-table tbody.pf-m-expanded tbody{--pf-c-table--tbody--after--BorderLeftWidth:0}.pf-m-grid.pf-c-table tbody>tr>:first-child:not(.pf-c-table__check):after{--pf-c-table__expandable-row--after--BorderLeftWidth:0;position:static;width:auto;background-color:transparent}.pf-m-grid.pf-c-table .pf-c-table__expandable-row{--pf-c-table--cell--responsive--PaddingTop:0;--pf-c-table--cell--responsive--PaddingRight:0;--pf-c-table--cell--responsive--PaddingBottom:0;--pf-c-table--cell--responsive--PaddingLeft:0;--pf-c-table--cell--PaddingRight:0;--pf-c-table--cell--PaddingLeft:0;display:block;max-height:var(--pf-c-table__expandable-row--MaxHeight);overflow-y:auto;border-bottom:none;box-shadow:none}.pf-m-grid.pf-c-table .pf-c-table__expandable-row>*{position:static;display:block}.pf-m-grid.pf-c-table .pf-c-table__expandable-row.pf-m-expanded{border-top-color:var(--pf-c-table--BorderColor)}.pf-m-grid.pf-c-table .pf-c-table__expandable-row>:first-child:not(.pf-c-table__check):after{content:none}.pf-m-grid.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,.pf-m-grid.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content{padding:0}.pf-m-grid.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded){display:none;visibility:hidden}.pf-m-grid.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content{padding-right:var(--pf-c-table__expandable-row-content--responsive--PaddingRight);padding-left:var(--pf-c-table__expandable-row-content--responsive--PaddingLeft)}.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action,.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,.pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle{width:auto;padding:0}.pf-m-grid.pf-c-table .pf-c-table__toggle{grid-row-start:20;grid-column:-1;justify-self:end;padding-right:0}.pf-m-grid.pf-c-table .pf-c-table__toggle:after{content:none}.pf-m-grid.pf-c-table .pf-c-table__button{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft)}.pf-m-grid.pf-c-table .pf-c-table__action,.pf-m-grid.pf-c-table .pf-c-table__check,.pf-m-grid.pf-c-table .pf-c-table__favorite{grid-row-start:1;grid-column-start:2}.pf-m-grid.pf-c-table .pf-c-table__check{margin-top:var(--pf-c-table__check--responsive--MarginTop);margin-left:var(--pf-c-table__check--responsive--MarginLeft)}.pf-m-grid.pf-c-table .pf-c-table__check~.pf-c-table__favorite{margin-left:var(--pf-c-table--m-grid__check--favorite--MarginLeft)}.pf-m-grid.pf-c-table .pf-c-table__check~.pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__check--favorite--action--MarginLeft)}.pf-m-grid.pf-c-table .pf-c-table__check~.pf-c-table__action{margin-left:var(--pf-c-table__action--responsive--MarginLeft)}.pf-m-grid.pf-c-table .pf-c-table__favorite{margin-top:var(--pf-c-table--m-grid__favorite--MarginTop)}.pf-m-grid.pf-c-table .pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__favorite--action--MarginLeft)}.pf-m-grid.pf-c-table .pf-c-table__action{margin-top:var(--pf-c-table--m-grid__action--MarginTop);text-align:right}@media screen and (max-width:576px){.pf-m-grid.pf-c-table .pf-c-table__action{grid-row-start:1;grid-column-start:2;margin-left:0}}.pf-m-grid.pf-c-table .pf-c-table__inline-edit-action{grid-column:2;grid-row:2}.pf-m-grid.pf-c-table .pf-c-table__toggle-icon{transition:var(--pf-c-table__toggle__icon--Transition)}.pf-c-button.pf-m-expanded>.pf-m-grid.pf-c-table .pf-c-table__toggle-icon{transform:rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate))}.pf-m-grid.pf-c-table .pf-m-nowrap{--pf-c-table--cell--Overflow:auto}.pf-m-grid.pf-c-table .pf-m-fit-content{width:auto;white-space:normal}.pf-m-grid.pf-c-table .pf-m-truncate{--pf-c-table--cell--MaxWidth:100%}.pf-m-grid.pf-c-table [class*=pf-m-width]{--pf-c-table--cell--Width:auto}@media screen and (max-width:768px){.pf-m-grid-md.pf-c-table{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft);--pf-c-table__favorite--c-button--MarginTop:auto;--pf-c-table__favorite--c-button--MarginRight:auto;--pf-c-table__favorite--c-button--MarginBottom:auto;--pf-c-table__favorite--c-button--MarginLeft:auto;display:grid;border:none}.pf-m-grid-md.pf-c-table tr>*{width:auto;min-width:0;max-width:none;overflow:visible;text-overflow:clip;white-space:normal}.pf-m-grid-md.pf-c-table .pf-c-table__text{position:relative;width:auto;min-width:0;max-width:none;overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-m-grid-md.pf-c-table thead{display:none;visibility:hidden}.pf-m-grid-md.pf-c-table tbody{display:block}.pf-m-grid-md.pf-c-table tbody:first-of-type{border-top:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-md.pf-c-table table.pf-m-compact>tbody{border-top:0}.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row){border-bottom:var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-md.pf-c-table tbody:last-of-type:not(:only-of-type)>tr,.pf-m-grid-md.pf-c-table tr:last-child{border-bottom-width:var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth)}.pf-m-grid-md.pf-c-table tbody.pf-m-expanded{border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-m-grid-md.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row){border-bottom:0}.pf-m-grid-md.pf-c-table tbody.pf-m-expanded:not(:last-of-type){border-bottom:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row){display:grid;grid-template-columns:1fr;height:auto;grid-auto-columns:max-content;grid-column-gap:var(--pf-c-table-tr--responsive--GridColumnGap);padding:var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft)}.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row)>*{padding:var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft)}.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row)>:first-child{--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--cell--first-child--responsive--PaddingTop)}.pf-m-grid-md.pf-c-table.pf-m-compact{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table--m-compact-tr--responsive--PaddingTop);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);--pf-c-table--cell--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);--pf-c-table__check--input--MarginTop:0}.pf-m-grid-md.pf-c-table.pf-m-compact .pf-c-table__action{margin-top:var(--pf-c-table--m-compact__action--responsive--MarginTop);margin-bottom:var(--pf-c-table--m-compact__action--responsive--MarginTop)}.pf-m-grid-md.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button{margin-bottom:var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom)}.pf-m-grid-md.pf-c-table .pf-c-table__icon>*{text-align:left}.pf-m-grid-md.pf-c-table [data-label]{--pf-c-table--cell--hidden-visible--Display:var(--pf-c-table--m-grid--cell--hidden-visible--Display);grid-column:1;grid-column-gap:var(--pf-c-table-td--responsive--GridColumnGap);grid-template-columns:1fr minmax(0,1.5fr);align-items:start}.pf-m-grid-md.pf-c-table [data-label]>*{grid-column:2}.pf-m-grid-md.pf-c-table [data-label]:before{font-weight:700;text-align:left;content:attr(data-label)}.pf-m-grid-md.pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:0}.pf-m-grid-md.pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:0}.pf-m-grid-md.pf-c-table .pf-c-table{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table-tr--responsive--nested-table--PaddingTop);--pf-c-table-tr--responsive--PaddingRight:var(--pf-c-table-tr--responsive--nested-table--PaddingRight);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);border:0}.pf-m-grid-md.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row)+tr:not(.pf-c-table__expandable-row){--pf-c-table-tr--responsive--PaddingTop:0}.pf-m-grid-md.pf-c-table .pf-c-table__compound-expansion-toggle{--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:0;--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0;--pf-c-table__compound-expansion-toggle__button--after--Top:100%}.pf-m-grid-md.pf-c-table tbody{position:relative}.pf-m-grid-md.pf-c-table tbody:after{position:absolute;top:0;bottom:0;left:0;content:"";border:0;border-left:var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor)}.pf-m-grid-md.pf-c-table tbody.pf-m-expanded{--pf-c-table--tbody--after--BorderLeftWidth:var(--pf-c-table--tbody--after--border-width--base)}.pf-m-grid-md.pf-c-table tbody.pf-m-expanded tbody{--pf-c-table--tbody--after--BorderLeftWidth:0}.pf-m-grid-md.pf-c-table tbody>tr>:first-child:not(.pf-c-table__check):after{--pf-c-table__expandable-row--after--BorderLeftWidth:0;position:static;width:auto;background-color:transparent}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row{--pf-c-table--cell--responsive--PaddingTop:0;--pf-c-table--cell--responsive--PaddingRight:0;--pf-c-table--cell--responsive--PaddingBottom:0;--pf-c-table--cell--responsive--PaddingLeft:0;--pf-c-table--cell--PaddingRight:0;--pf-c-table--cell--PaddingLeft:0;display:block;max-height:var(--pf-c-table__expandable-row--MaxHeight);overflow-y:auto;border-bottom:none;box-shadow:none}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row>*{position:static;display:block}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row.pf-m-expanded{border-top-color:var(--pf-c-table--BorderColor)}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row>:first-child:not(.pf-c-table__check):after{content:none}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content{padding:0}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded){display:none;visibility:hidden}.pf-m-grid-md.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content{padding-right:var(--pf-c-table__expandable-row-content--responsive--PaddingRight);padding-left:var(--pf-c-table__expandable-row-content--responsive--PaddingLeft)}.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action,.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,.pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle{width:auto;padding:0}.pf-m-grid-md.pf-c-table .pf-c-table__toggle{grid-row-start:20;grid-column:-1;justify-self:end;padding-right:0}.pf-m-grid-md.pf-c-table .pf-c-table__toggle:after{content:none}.pf-m-grid-md.pf-c-table .pf-c-table__button{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft)}.pf-m-grid-md.pf-c-table .pf-c-table__action,.pf-m-grid-md.pf-c-table .pf-c-table__check,.pf-m-grid-md.pf-c-table .pf-c-table__favorite{grid-row-start:1;grid-column-start:2}.pf-m-grid-md.pf-c-table .pf-c-table__check{margin-top:var(--pf-c-table__check--responsive--MarginTop);margin-left:var(--pf-c-table__check--responsive--MarginLeft)}.pf-m-grid-md.pf-c-table .pf-c-table__check~.pf-c-table__favorite{margin-left:var(--pf-c-table--m-grid__check--favorite--MarginLeft)}.pf-m-grid-md.pf-c-table .pf-c-table__check~.pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__check--favorite--action--MarginLeft)}.pf-m-grid-md.pf-c-table .pf-c-table__check~.pf-c-table__action{margin-left:var(--pf-c-table__action--responsive--MarginLeft)}.pf-m-grid-md.pf-c-table .pf-c-table__favorite{margin-top:var(--pf-c-table--m-grid__favorite--MarginTop)}.pf-m-grid-md.pf-c-table .pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__favorite--action--MarginLeft)}.pf-m-grid-md.pf-c-table .pf-c-table__action{margin-top:var(--pf-c-table--m-grid__action--MarginTop);text-align:right}}@media screen and (max-width:768px) and (max-width:576px){.pf-m-grid-md.pf-c-table .pf-c-table__action{grid-row-start:1;grid-column-start:2;margin-left:0}}@media screen and (max-width:768px){.pf-m-grid-md.pf-c-table .pf-c-table__inline-edit-action{grid-column:2;grid-row:2}.pf-m-grid-md.pf-c-table .pf-c-table__toggle-icon{transition:var(--pf-c-table__toggle__icon--Transition)}.pf-c-button.pf-m-expanded>.pf-m-grid-md.pf-c-table .pf-c-table__toggle-icon{transform:rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate))}.pf-m-grid-md.pf-c-table .pf-m-nowrap{--pf-c-table--cell--Overflow:auto}.pf-m-grid-md.pf-c-table .pf-m-fit-content{width:auto;white-space:normal}.pf-m-grid-md.pf-c-table .pf-m-truncate{--pf-c-table--cell--MaxWidth:100%}.pf-m-grid-md.pf-c-table [class*=pf-m-width]{--pf-c-table--cell--Width:auto}}@media screen and (max-width:992px){.pf-m-grid-lg.pf-c-table{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft);--pf-c-table__favorite--c-button--MarginTop:auto;--pf-c-table__favorite--c-button--MarginRight:auto;--pf-c-table__favorite--c-button--MarginBottom:auto;--pf-c-table__favorite--c-button--MarginLeft:auto;display:grid;border:none}.pf-m-grid-lg.pf-c-table tr>*{width:auto;min-width:0;max-width:none;overflow:visible;text-overflow:clip;white-space:normal}.pf-m-grid-lg.pf-c-table .pf-c-table__text{position:relative;width:auto;min-width:0;max-width:none;overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-m-grid-lg.pf-c-table thead{display:none;visibility:hidden}.pf-m-grid-lg.pf-c-table tbody{display:block}.pf-m-grid-lg.pf-c-table tbody:first-of-type{border-top:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-lg.pf-c-table table.pf-m-compact>tbody{border-top:0}.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row){border-bottom:var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-lg.pf-c-table tbody:last-of-type:not(:only-of-type)>tr,.pf-m-grid-lg.pf-c-table tr:last-child{border-bottom-width:var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth)}.pf-m-grid-lg.pf-c-table tbody.pf-m-expanded{border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-m-grid-lg.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row){border-bottom:0}.pf-m-grid-lg.pf-c-table tbody.pf-m-expanded:not(:last-of-type){border-bottom:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row){display:grid;grid-template-columns:1fr;height:auto;grid-auto-columns:max-content;grid-column-gap:var(--pf-c-table-tr--responsive--GridColumnGap);padding:var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft)}.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row)>*{padding:var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft)}.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row)>:first-child{--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--cell--first-child--responsive--PaddingTop)}.pf-m-grid-lg.pf-c-table.pf-m-compact{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table--m-compact-tr--responsive--PaddingTop);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);--pf-c-table--cell--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);--pf-c-table__check--input--MarginTop:0}.pf-m-grid-lg.pf-c-table.pf-m-compact .pf-c-table__action{margin-top:var(--pf-c-table--m-compact__action--responsive--MarginTop);margin-bottom:var(--pf-c-table--m-compact__action--responsive--MarginTop)}.pf-m-grid-lg.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button{margin-bottom:var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom)}.pf-m-grid-lg.pf-c-table .pf-c-table__icon>*{text-align:left}.pf-m-grid-lg.pf-c-table [data-label]{--pf-c-table--cell--hidden-visible--Display:var(--pf-c-table--m-grid--cell--hidden-visible--Display);grid-column:1;grid-column-gap:var(--pf-c-table-td--responsive--GridColumnGap);grid-template-columns:1fr minmax(0,1.5fr);align-items:start}.pf-m-grid-lg.pf-c-table [data-label]>*{grid-column:2}.pf-m-grid-lg.pf-c-table [data-label]:before{font-weight:700;text-align:left;content:attr(data-label)}.pf-m-grid-lg.pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:0}.pf-m-grid-lg.pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:0}.pf-m-grid-lg.pf-c-table .pf-c-table{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table-tr--responsive--nested-table--PaddingTop);--pf-c-table-tr--responsive--PaddingRight:var(--pf-c-table-tr--responsive--nested-table--PaddingRight);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);border:0}.pf-m-grid-lg.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row)+tr:not(.pf-c-table__expandable-row){--pf-c-table-tr--responsive--PaddingTop:0}.pf-m-grid-lg.pf-c-table .pf-c-table__compound-expansion-toggle{--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:0;--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0;--pf-c-table__compound-expansion-toggle__button--after--Top:100%}.pf-m-grid-lg.pf-c-table tbody{position:relative}.pf-m-grid-lg.pf-c-table tbody:after{position:absolute;top:0;bottom:0;left:0;content:"";border:0;border-left:var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor)}.pf-m-grid-lg.pf-c-table tbody.pf-m-expanded{--pf-c-table--tbody--after--BorderLeftWidth:var(--pf-c-table--tbody--after--border-width--base)}.pf-m-grid-lg.pf-c-table tbody.pf-m-expanded tbody{--pf-c-table--tbody--after--BorderLeftWidth:0}.pf-m-grid-lg.pf-c-table tbody>tr>:first-child:not(.pf-c-table__check):after{--pf-c-table__expandable-row--after--BorderLeftWidth:0;position:static;width:auto;background-color:transparent}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row{--pf-c-table--cell--responsive--PaddingTop:0;--pf-c-table--cell--responsive--PaddingRight:0;--pf-c-table--cell--responsive--PaddingBottom:0;--pf-c-table--cell--responsive--PaddingLeft:0;--pf-c-table--cell--PaddingRight:0;--pf-c-table--cell--PaddingLeft:0;display:block;max-height:var(--pf-c-table__expandable-row--MaxHeight);overflow-y:auto;border-bottom:none;box-shadow:none}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row>*{position:static;display:block}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row.pf-m-expanded{border-top-color:var(--pf-c-table--BorderColor)}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row>:first-child:not(.pf-c-table__check):after{content:none}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content{padding:0}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded){display:none;visibility:hidden}.pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content{padding-right:var(--pf-c-table__expandable-row-content--responsive--PaddingRight);padding-left:var(--pf-c-table__expandable-row-content--responsive--PaddingLeft)}.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action,.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,.pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle{width:auto;padding:0}.pf-m-grid-lg.pf-c-table .pf-c-table__toggle{grid-row-start:20;grid-column:-1;justify-self:end;padding-right:0}.pf-m-grid-lg.pf-c-table .pf-c-table__toggle:after{content:none}.pf-m-grid-lg.pf-c-table .pf-c-table__button{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft)}.pf-m-grid-lg.pf-c-table .pf-c-table__action,.pf-m-grid-lg.pf-c-table .pf-c-table__check,.pf-m-grid-lg.pf-c-table .pf-c-table__favorite{grid-row-start:1;grid-column-start:2}.pf-m-grid-lg.pf-c-table .pf-c-table__check{margin-top:var(--pf-c-table__check--responsive--MarginTop);margin-left:var(--pf-c-table__check--responsive--MarginLeft)}.pf-m-grid-lg.pf-c-table .pf-c-table__check~.pf-c-table__favorite{margin-left:var(--pf-c-table--m-grid__check--favorite--MarginLeft)}.pf-m-grid-lg.pf-c-table .pf-c-table__check~.pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__check--favorite--action--MarginLeft)}.pf-m-grid-lg.pf-c-table .pf-c-table__check~.pf-c-table__action{margin-left:var(--pf-c-table__action--responsive--MarginLeft)}.pf-m-grid-lg.pf-c-table .pf-c-table__favorite{margin-top:var(--pf-c-table--m-grid__favorite--MarginTop)}.pf-m-grid-lg.pf-c-table .pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__favorite--action--MarginLeft)}.pf-m-grid-lg.pf-c-table .pf-c-table__action{margin-top:var(--pf-c-table--m-grid__action--MarginTop);text-align:right}}@media screen and (max-width:992px) and (max-width:576px){.pf-m-grid-lg.pf-c-table .pf-c-table__action{grid-row-start:1;grid-column-start:2;margin-left:0}}@media screen and (max-width:992px){.pf-m-grid-lg.pf-c-table .pf-c-table__inline-edit-action{grid-column:2;grid-row:2}.pf-m-grid-lg.pf-c-table .pf-c-table__toggle-icon{transition:var(--pf-c-table__toggle__icon--Transition)}.pf-c-button.pf-m-expanded>.pf-m-grid-lg.pf-c-table .pf-c-table__toggle-icon{transform:rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate))}.pf-m-grid-lg.pf-c-table .pf-m-nowrap{--pf-c-table--cell--Overflow:auto}.pf-m-grid-lg.pf-c-table .pf-m-fit-content{width:auto;white-space:normal}.pf-m-grid-lg.pf-c-table .pf-m-truncate{--pf-c-table--cell--MaxWidth:100%}.pf-m-grid-lg.pf-c-table [class*=pf-m-width]{--pf-c-table--cell--Width:auto}}@media screen and (max-width:1200px){.pf-m-grid-xl.pf-c-table{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft);--pf-c-table__favorite--c-button--MarginTop:auto;--pf-c-table__favorite--c-button--MarginRight:auto;--pf-c-table__favorite--c-button--MarginBottom:auto;--pf-c-table__favorite--c-button--MarginLeft:auto;display:grid;border:none}.pf-m-grid-xl.pf-c-table tr>*{width:auto;min-width:0;max-width:none;overflow:visible;text-overflow:clip;white-space:normal}.pf-m-grid-xl.pf-c-table .pf-c-table__text{position:relative;width:auto;min-width:0;max-width:none;overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-m-grid-xl.pf-c-table thead{display:none;visibility:hidden}.pf-m-grid-xl.pf-c-table tbody{display:block}.pf-m-grid-xl.pf-c-table tbody:first-of-type{border-top:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-xl.pf-c-table table.pf-m-compact>tbody{border-top:0}.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row){border-bottom:var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-xl.pf-c-table tbody:last-of-type:not(:only-of-type)>tr,.pf-m-grid-xl.pf-c-table tr:last-child{border-bottom-width:var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth)}.pf-m-grid-xl.pf-c-table tbody.pf-m-expanded{border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-m-grid-xl.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row){border-bottom:0}.pf-m-grid-xl.pf-c-table tbody.pf-m-expanded:not(:last-of-type){border-bottom:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row){display:grid;grid-template-columns:1fr;height:auto;grid-auto-columns:max-content;grid-column-gap:var(--pf-c-table-tr--responsive--GridColumnGap);padding:var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft)}.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row)>*{padding:var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft)}.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row)>:first-child{--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--cell--first-child--responsive--PaddingTop)}.pf-m-grid-xl.pf-c-table.pf-m-compact{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table--m-compact-tr--responsive--PaddingTop);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);--pf-c-table--cell--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);--pf-c-table__check--input--MarginTop:0}.pf-m-grid-xl.pf-c-table.pf-m-compact .pf-c-table__action{margin-top:var(--pf-c-table--m-compact__action--responsive--MarginTop);margin-bottom:var(--pf-c-table--m-compact__action--responsive--MarginTop)}.pf-m-grid-xl.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button{margin-bottom:var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom)}.pf-m-grid-xl.pf-c-table .pf-c-table__icon>*{text-align:left}.pf-m-grid-xl.pf-c-table [data-label]{--pf-c-table--cell--hidden-visible--Display:var(--pf-c-table--m-grid--cell--hidden-visible--Display);grid-column:1;grid-column-gap:var(--pf-c-table-td--responsive--GridColumnGap);grid-template-columns:1fr minmax(0,1.5fr);align-items:start}.pf-m-grid-xl.pf-c-table [data-label]>*{grid-column:2}.pf-m-grid-xl.pf-c-table [data-label]:before{font-weight:700;text-align:left;content:attr(data-label)}.pf-m-grid-xl.pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:0}.pf-m-grid-xl.pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:0}.pf-m-grid-xl.pf-c-table .pf-c-table{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table-tr--responsive--nested-table--PaddingTop);--pf-c-table-tr--responsive--PaddingRight:var(--pf-c-table-tr--responsive--nested-table--PaddingRight);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);border:0}.pf-m-grid-xl.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row)+tr:not(.pf-c-table__expandable-row){--pf-c-table-tr--responsive--PaddingTop:0}.pf-m-grid-xl.pf-c-table .pf-c-table__compound-expansion-toggle{--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:0;--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0;--pf-c-table__compound-expansion-toggle__button--after--Top:100%}.pf-m-grid-xl.pf-c-table tbody{position:relative}.pf-m-grid-xl.pf-c-table tbody:after{position:absolute;top:0;bottom:0;left:0;content:"";border:0;border-left:var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor)}.pf-m-grid-xl.pf-c-table tbody.pf-m-expanded{--pf-c-table--tbody--after--BorderLeftWidth:var(--pf-c-table--tbody--after--border-width--base)}.pf-m-grid-xl.pf-c-table tbody.pf-m-expanded tbody{--pf-c-table--tbody--after--BorderLeftWidth:0}.pf-m-grid-xl.pf-c-table tbody>tr>:first-child:not(.pf-c-table__check):after{--pf-c-table__expandable-row--after--BorderLeftWidth:0;position:static;width:auto;background-color:transparent}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row{--pf-c-table--cell--responsive--PaddingTop:0;--pf-c-table--cell--responsive--PaddingRight:0;--pf-c-table--cell--responsive--PaddingBottom:0;--pf-c-table--cell--responsive--PaddingLeft:0;--pf-c-table--cell--PaddingRight:0;--pf-c-table--cell--PaddingLeft:0;display:block;max-height:var(--pf-c-table__expandable-row--MaxHeight);overflow-y:auto;border-bottom:none;box-shadow:none}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row>*{position:static;display:block}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row.pf-m-expanded{border-top-color:var(--pf-c-table--BorderColor)}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row>:first-child:not(.pf-c-table__check):after{content:none}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content{padding:0}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded){display:none;visibility:hidden}.pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content{padding-right:var(--pf-c-table__expandable-row-content--responsive--PaddingRight);padding-left:var(--pf-c-table__expandable-row-content--responsive--PaddingLeft)}.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action,.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,.pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle{width:auto;padding:0}.pf-m-grid-xl.pf-c-table .pf-c-table__toggle{grid-row-start:20;grid-column:-1;justify-self:end;padding-right:0}.pf-m-grid-xl.pf-c-table .pf-c-table__toggle:after{content:none}.pf-m-grid-xl.pf-c-table .pf-c-table__button{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft)}.pf-m-grid-xl.pf-c-table .pf-c-table__action,.pf-m-grid-xl.pf-c-table .pf-c-table__check,.pf-m-grid-xl.pf-c-table .pf-c-table__favorite{grid-row-start:1;grid-column-start:2}.pf-m-grid-xl.pf-c-table .pf-c-table__check{margin-top:var(--pf-c-table__check--responsive--MarginTop);margin-left:var(--pf-c-table__check--responsive--MarginLeft)}.pf-m-grid-xl.pf-c-table .pf-c-table__check~.pf-c-table__favorite{margin-left:var(--pf-c-table--m-grid__check--favorite--MarginLeft)}.pf-m-grid-xl.pf-c-table .pf-c-table__check~.pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__check--favorite--action--MarginLeft)}.pf-m-grid-xl.pf-c-table .pf-c-table__check~.pf-c-table__action{margin-left:var(--pf-c-table__action--responsive--MarginLeft)}.pf-m-grid-xl.pf-c-table .pf-c-table__favorite{margin-top:var(--pf-c-table--m-grid__favorite--MarginTop)}.pf-m-grid-xl.pf-c-table .pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__favorite--action--MarginLeft)}.pf-m-grid-xl.pf-c-table .pf-c-table__action{margin-top:var(--pf-c-table--m-grid__action--MarginTop);text-align:right}}@media screen and (max-width:1200px) and (max-width:576px){.pf-m-grid-xl.pf-c-table .pf-c-table__action{grid-row-start:1;grid-column-start:2;margin-left:0}}@media screen and (max-width:1200px){.pf-m-grid-xl.pf-c-table .pf-c-table__inline-edit-action{grid-column:2;grid-row:2}.pf-m-grid-xl.pf-c-table .pf-c-table__toggle-icon{transition:var(--pf-c-table__toggle__icon--Transition)}.pf-c-button.pf-m-expanded>.pf-m-grid-xl.pf-c-table .pf-c-table__toggle-icon{transform:rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate))}.pf-m-grid-xl.pf-c-table .pf-m-nowrap{--pf-c-table--cell--Overflow:auto}.pf-m-grid-xl.pf-c-table .pf-m-fit-content{width:auto;white-space:normal}.pf-m-grid-xl.pf-c-table .pf-m-truncate{--pf-c-table--cell--MaxWidth:100%}.pf-m-grid-xl.pf-c-table [class*=pf-m-width]{--pf-c-table--cell--Width:auto}}@media screen and (max-width:1450px){.pf-m-grid-2xl.pf-c-table{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft);--pf-c-table__favorite--c-button--MarginTop:auto;--pf-c-table__favorite--c-button--MarginRight:auto;--pf-c-table__favorite--c-button--MarginBottom:auto;--pf-c-table__favorite--c-button--MarginLeft:auto;display:grid;border:none}.pf-m-grid-2xl.pf-c-table tr>*{width:auto;min-width:0;max-width:none;overflow:visible;text-overflow:clip;white-space:normal}.pf-m-grid-2xl.pf-c-table .pf-c-table__text{position:relative;width:auto;min-width:0;max-width:none;overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-m-grid-2xl.pf-c-table thead{display:none;visibility:hidden}.pf-m-grid-2xl.pf-c-table tbody{display:block}.pf-m-grid-2xl.pf-c-table tbody:first-of-type{border-top:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-2xl.pf-c-table table.pf-m-compact>tbody{border-top:0}.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row){border-bottom:var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-2xl.pf-c-table tbody:last-of-type:not(:only-of-type)>tr,.pf-m-grid-2xl.pf-c-table tr:last-child{border-bottom-width:var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth)}.pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded{border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row){border-bottom:0}.pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded:not(:last-of-type){border-bottom:var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor)}.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row){display:grid;grid-template-columns:1fr;height:auto;grid-auto-columns:max-content;grid-column-gap:var(--pf-c-table-tr--responsive--GridColumnGap);padding:var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft)}.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row)>*{padding:var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft)}.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row)>:first-child{--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--cell--first-child--responsive--PaddingTop)}.pf-m-grid-2xl.pf-c-table.pf-m-compact{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table--m-compact-tr--responsive--PaddingTop);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);--pf-c-table--cell--responsive--PaddingTop:var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);--pf-c-table--cell--responsive--PaddingBottom:var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);--pf-c-table__check--input--MarginTop:0}.pf-m-grid-2xl.pf-c-table.pf-m-compact .pf-c-table__action{margin-top:var(--pf-c-table--m-compact__action--responsive--MarginTop);margin-bottom:var(--pf-c-table--m-compact__action--responsive--MarginTop)}.pf-m-grid-2xl.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button{margin-bottom:var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom)}.pf-m-grid-2xl.pf-c-table .pf-c-table__icon>*{text-align:left}.pf-m-grid-2xl.pf-c-table [data-label]{--pf-c-table--cell--hidden-visible--Display:var(--pf-c-table--m-grid--cell--hidden-visible--Display);grid-column:1;grid-column-gap:var(--pf-c-table-td--responsive--GridColumnGap);grid-template-columns:1fr minmax(0,1.5fr);align-items:start}.pf-m-grid-2xl.pf-c-table [data-label]>*{grid-column:2}.pf-m-grid-2xl.pf-c-table [data-label]:before{font-weight:700;text-align:left;content:attr(data-label)}.pf-m-grid-2xl.pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:0}.pf-m-grid-2xl.pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:0}.pf-m-grid-2xl.pf-c-table .pf-c-table{--pf-c-table-tr--responsive--PaddingTop:var(--pf-c-table-tr--responsive--nested-table--PaddingTop);--pf-c-table-tr--responsive--PaddingRight:var(--pf-c-table-tr--responsive--nested-table--PaddingRight);--pf-c-table-tr--responsive--PaddingBottom:var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);--pf-c-table-tr--responsive--PaddingLeft:var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);border:0}.pf-m-grid-2xl.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row)+tr:not(.pf-c-table__expandable-row){--pf-c-table-tr--responsive--PaddingTop:0}.pf-m-grid-2xl.pf-c-table .pf-c-table__compound-expansion-toggle{--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:0;--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0;--pf-c-table__compound-expansion-toggle__button--after--Top:100%}.pf-m-grid-2xl.pf-c-table tbody{position:relative}.pf-m-grid-2xl.pf-c-table tbody:after{position:absolute;top:0;bottom:0;left:0;content:"";border:0;border-left:var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor)}.pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded{--pf-c-table--tbody--after--BorderLeftWidth:var(--pf-c-table--tbody--after--border-width--base)}.pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded tbody{--pf-c-table--tbody--after--BorderLeftWidth:0}.pf-m-grid-2xl.pf-c-table tbody>tr>:first-child:not(.pf-c-table__check):after{--pf-c-table__expandable-row--after--BorderLeftWidth:0;position:static;width:auto;background-color:transparent}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row{--pf-c-table--cell--responsive--PaddingTop:0;--pf-c-table--cell--responsive--PaddingRight:0;--pf-c-table--cell--responsive--PaddingBottom:0;--pf-c-table--cell--responsive--PaddingLeft:0;--pf-c-table--cell--PaddingRight:0;--pf-c-table--cell--PaddingLeft:0;display:block;max-height:var(--pf-c-table__expandable-row--MaxHeight);overflow-y:auto;border-bottom:none;box-shadow:none}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row>*{position:static;display:block}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row.pf-m-expanded{border-top-color:var(--pf-c-table--BorderColor)}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row>:first-child:not(.pf-c-table__check):after{content:none}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content{padding:0}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded){display:none;visibility:hidden}.pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content{padding-right:var(--pf-c-table__expandable-row-content--responsive--PaddingRight);padding-left:var(--pf-c-table__expandable-row-content--responsive--PaddingLeft)}.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action,.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,.pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle{width:auto;padding:0}.pf-m-grid-2xl.pf-c-table .pf-c-table__toggle{grid-row-start:20;grid-column:-1;justify-self:end;padding-right:0}.pf-m-grid-2xl.pf-c-table .pf-c-table__toggle:after{content:none}.pf-m-grid-2xl.pf-c-table .pf-c-table__button{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-grid--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-grid--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-grid--cell--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-grid--cell--PaddingLeft)}.pf-m-grid-2xl.pf-c-table .pf-c-table__action,.pf-m-grid-2xl.pf-c-table .pf-c-table__check,.pf-m-grid-2xl.pf-c-table .pf-c-table__favorite{grid-row-start:1;grid-column-start:2}.pf-m-grid-2xl.pf-c-table .pf-c-table__check{margin-top:var(--pf-c-table__check--responsive--MarginTop);margin-left:var(--pf-c-table__check--responsive--MarginLeft)}.pf-m-grid-2xl.pf-c-table .pf-c-table__check~.pf-c-table__favorite{margin-left:var(--pf-c-table--m-grid__check--favorite--MarginLeft)}.pf-m-grid-2xl.pf-c-table .pf-c-table__check~.pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__check--favorite--action--MarginLeft)}.pf-m-grid-2xl.pf-c-table .pf-c-table__check~.pf-c-table__action{margin-left:var(--pf-c-table__action--responsive--MarginLeft)}.pf-m-grid-2xl.pf-c-table .pf-c-table__favorite{margin-top:var(--pf-c-table--m-grid__favorite--MarginTop)}.pf-m-grid-2xl.pf-c-table .pf-c-table__favorite~.pf-c-table__action{margin-left:var(--pf-c-table--m-grid__favorite--action--MarginLeft)}.pf-m-grid-2xl.pf-c-table .pf-c-table__action{margin-top:var(--pf-c-table--m-grid__action--MarginTop);text-align:right}}@media screen and (max-width:1450px) and (max-width:576px){.pf-m-grid-2xl.pf-c-table .pf-c-table__action{grid-row-start:1;grid-column-start:2;margin-left:0}}@media screen and (max-width:1450px){.pf-m-grid-2xl.pf-c-table .pf-c-table__inline-edit-action{grid-column:2;grid-row:2}.pf-m-grid-2xl.pf-c-table .pf-c-table__toggle-icon{transition:var(--pf-c-table__toggle__icon--Transition)}.pf-c-button.pf-m-expanded>.pf-m-grid-2xl.pf-c-table .pf-c-table__toggle-icon{transform:rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate))}.pf-m-grid-2xl.pf-c-table .pf-m-nowrap{--pf-c-table--cell--Overflow:auto}.pf-m-grid-2xl.pf-c-table .pf-m-fit-content{width:auto;white-space:normal}.pf-m-grid-2xl.pf-c-table .pf-m-truncate{--pf-c-table--cell--MaxWidth:100%}.pf-m-grid-2xl.pf-c-table [class*=pf-m-width]{--pf-c-table--cell--Width:auto}}.pf-c-table{--pf-c-table--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-table--BorderColor:var(--pf-global--BorderColor--100);--pf-c-table--border-width--base:var(--pf-global--BorderWidth--sm);--pf-c-table-caption--FontSize:var(--pf-global--FontSize--sm);--pf-c-table-caption--Color:var(--pf-global--Color--200);--pf-c-table-caption--PaddingTop:var(--pf-global--spacer--md);--pf-c-table-caption--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table-caption--PaddingBottom:var(--pf-global--spacer--md);--pf-c-table-caption--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table-caption--xl--PaddingRight:var(--pf-global--spacer--md);--pf-c-table-caption--xl--PaddingLeft:var(--pf-global--spacer--md);--pf-c-table--thead--cell--FontSize:var(--pf-global--FontSize--sm);--pf-c-table--thead--cell--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-table--tbody--cell--PaddingTop:var(--pf-global--spacer--lg);--pf-c-table--tbody--cell--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-table--cell--FontSize:var(--pf-global--FontSize--md);--pf-c-table--cell--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-table--cell--Color:var(--pf-global--Color--100);--pf-c-table--cell--PaddingTop:var(--pf-global--spacer--md);--pf-c-table--cell--PaddingRight:var(--pf-global--spacer--md);--pf-c-table--cell--PaddingBottom:var(--pf-global--spacer--md);--pf-c-table--cell--PaddingLeft:var(--pf-global--spacer--md);--pf-c-table--cell--first-last-child--PaddingLeft:var(--pf-global--spacer--md);--pf-c-table--cell--first-last-child--PaddingRight:var(--pf-global--spacer--md);--pf-c-table--cell--first-last-child--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table--cell--first-last-child--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table--cell--MinWidth:0;--pf-c-table--cell--MaxWidth:none;--pf-c-table--cell--Width:auto;--pf-c-table--cell--Overflow:visible;--pf-c-table--cell--TextOverflow:clip;--pf-c-table--cell--WhiteSpace:normal;--pf-c-table--cell--WordBreak:normal;--pf-c-table--cell--m-help--MinWidth:11ch;--pf-c-table--m-truncate--cell--MaxWidth:1px;--pf-c-table--m-truncate--cell--MinWidth:calc(5ch + var(--pf-c-table--cell--PaddingRight) + var(--pf-c-table--cell--PaddingLeft));--pf-c-table--cell--hidden-visible--Display:table-cell;--pf-c-table__toggle--c-button--MarginTop:-0.375rem;--pf-c-table__toggle--c-button__toggle-icon--Rotate:270deg;--pf-c-table__toggle--c-button__toggle-icon--Transition:.2s ease-in 0s;--pf-c-table__toggle--c-button--m-expanded__toggle-icon--Rotate:360deg;--pf-c-table__button--BackgroundColor:transparent;--pf-c-table__button--Color:var(--pf-global--Color--100);--pf-c-table__button--hover--Color:var(--pf-global--Color--100);--pf-c-table__button--focus--Color:var(--pf-global--Color--100);--pf-c-table__button--active--Color:var(--pf-global--Color--100);--pf-c-table__button--OutlineOffset:calc(var(--pf-global--BorderWidth--lg)*-1);--pf-c-table--m-compact__toggle--PaddingTop:0;--pf-c-table--m-compact__toggle--PaddingBottom:0;--pf-c-table__check--input--MarginTop:0.25rem;--pf-c-table__check--input--FontSize:var(--pf-global--FontSize--md);--pf-c-table--cell--m-favorite--Color:var(--pf-global--Color--light-300);--pf-c-table__favorite--c-button--Color:var(--pf-global--Color--light-300);--pf-c-table__favorite--c-button--FontSize:var(--pf-global--FontSize--sm);--pf-c-table__favorite--c-button--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-table__favorite--c-button--MarginRight:calc(var(--pf-global--spacer--md)*-1);--pf-c-table__favorite--c-button--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-table__favorite--c-button--MarginLeft:calc(var(--pf-global--spacer--md)*-1);--pf-c-table__favorite--m-favorited--c-button--Color:var(--pf-global--palette--gold-400);--pf-c-table__sort--m-favorite__button__text--Color:var(--pf-global--Color--200);--pf-c-table__sort--m-favorite__button--hover__text--Color:var(--pf-global--Color--100);--pf-c-table__sort--m-favorite__button--focus__text--Color:var(--pf-global--Color--100);--pf-c-table__sort--m-favorite__button--active__text--Color:var(--pf-global--Color--100);--pf-c-table__action--PaddingTop:0;--pf-c-table__action--PaddingRight:0;--pf-c-table__action--PaddingBottom:0;--pf-c-table__action--PaddingLeft:0;--pf-c-table__inline-edit-action--PaddingTop:0;--pf-c-table__inline-edit-action--PaddingRight:0;--pf-c-table__inline-edit-action--PaddingBottom:0;--pf-c-table__inline-edit-action--PaddingLeft:0;--pf-c-table__expandable-row--Transition:var(--pf-global--Transition);--pf-c-table__expandable-row--MaxHeight:28.125rem;--pf-c-table__expandable-row-content--Transition:var(--pf-global--Transition);--pf-c-table__expandable-row-content--PaddingTop:var(--pf-global--spacer--lg);--pf-c-table__expandable-row-content--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-table__expandable-row--after--Top:calc(var(--pf-c-table--border-width--base)*-1);--pf-c-table__expandable-row--after--Bottom:calc(var(--pf-c-table--border-width--base)*-1);--pf-c-table__expandable-row--after--border-width--base:var(--pf-global--BorderWidth--lg);--pf-c-table__expandable-row--after--BorderLeftWidth:0;--pf-c-table__expandable-row--after--BorderColor:var(--pf-global--active-color--100);--pf-c-table__icon-inline--MarginRight:var(--pf-global--spacer--sm);--pf-c-table__sort--MinWidth:calc(6ch + var(--pf-c-table--cell--PaddingRight) + var(--pf-c-table--cell--PaddingLeft) + var(--pf-c-table__sort-indicator--MarginLeft));--pf-c-table__sort__button--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-table__sort__button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-table__sort__button--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-table__sort__button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-table__sort__button--MarginTop:calc(var(--pf-c-table__sort__button--PaddingTop)*-1);--pf-c-table__sort__button--MarginBottom:calc(var(--pf-c-table__sort__button--PaddingBottom)*-1);--pf-c-table__sort__button--MarginLeft:calc(var(--pf-c-table__sort__button--PaddingLeft)*-1);--pf-c-table__sort__button--Color:var(--pf-global--Color--100);--pf-c-table__sort--m-selected__button--Color:var(--pf-global--active-color--100);--pf-c-table__sort--m-help--MinWidth:15ch;--pf-c-table__sort__button__text--Color:currentColor;--pf-c-table__sort__button--hover__text--Color:currentColor;--pf-c-table__sort__button--focus__text--Color:currentColor;--pf-c-table__sort__button--active__text--Color:currentColor;--pf-c-table__sort-indicator--Color:var(--pf-global--disabled-color--200);--pf-c-table__sort-indicator--MarginLeft:var(--pf-global--spacer--md);--pf-c-table__sort--m-selected__sort-indicator--Color:var(--pf-global--active-color--100);--pf-c-table__sort__button--hover__sort-indicator--Color:var(--pf-global--Color--100);--pf-c-table__sort__button--active__sort-indicator--Color:var(--pf-global--Color--100);--pf-c-table__sort__button--focus__sort-indicator--Color:var(--pf-global--Color--100);--pf-c-table--th--m-help--MinWidth:11ch;--pf-c-table__column-help--MarginLeft:var(--pf-global--spacer--xs);--pf-c-table__column-help--TranslateY:0.125rem;--pf-c-table__column-help--c-button--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-table__column-help--c-button--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-table__column-help--c-button--PaddingRight:var(--pf-global--spacer--sm);--pf-c-table__column-help--c-button--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-table__compound-expansion-toggle__button--Color:var(--pf-global--active-color--100);--pf-c-table__compound-expansion-toggle__button--hover--Color:var(--pf-global--link--Color--hover);--pf-c-table__compound-expansion-toggle__button--focus--Color:var(--pf-global--link--Color--hover);--pf-c-table__compound-expansion-toggle__button--active--Color:var(--pf-global--link--Color--hover);--pf-c-table__compound-expansion-toggle__button--before--border-width--base:var(--pf-global--BorderWidth--sm);--pf-c-table__compound-expansion-toggle__button--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:0;--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0;--pf-c-table__compound-expansion-toggle__button--before--Bottom:calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base)*-1);--pf-c-table__compound-expansion-toggle__button--before--Left:calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base)*-1);--pf-c-table__compound-expansion-toggle__button--after--border-width--base:var(--pf-global--BorderWidth--lg);--pf-c-table__compound-expansion-toggle__button--after--BorderColor:var(--pf-global--primary-color--100);--pf-c-table__compound-expansion-toggle__button--after--BorderTopWidth:0;--pf-c-table__compound-expansion-toggle__button--after--Top:calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base)*-1);--pf-c-table__compound-expansion-toggle__button--after--Left:calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base)*-1);--pf-c-table--m-compact-th--PaddingTop:calc(var(--pf-global--spacer--sm) + var(--pf-global--spacer--xs));--pf-c-table--m-compact-th--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-table--m-compact--cell--PaddingTop:var(--pf-global--spacer--sm);--pf-c-table--m-compact--cell--PaddingRight:var(--pf-global--spacer--sm);--pf-c-table--m-compact--cell--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-table--m-compact--cell--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-table--m-compact--cell--first-last-child--PaddingLeft:var(--pf-global--spacer--md);--pf-c-table--m-compact--cell--first-last-child--PaddingRight:var(--pf-global--spacer--md);--pf-c-table--m-compact--cell--first-last-child--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table--m-compact--cell--first-last-child--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table--m-compact--FontSize:var(--pf-global--FontSize--sm);--pf-c-table--m-compact__expandable-row-content--PaddingTop:var(--pf-global--spacer--lg);--pf-c-table--m-compact__expandable-row-content--PaddingRight:var(--pf-global--spacer--lg);--pf-c-table--m-compact__expandable-row-content--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-table--m-compact__expandable-row-content--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-table--nested--first-last-child--PaddingRight:var(--pf-global--spacer--3xl);--pf-c-table--nested--first-last-child--PaddingLeft:var(--pf-global--spacer--3xl);--pf-c-table__expandable-row--m-expanded--BorderBottomColor:var(--pf-global--BorderColor--100);color:var(--pf-global--Color--100);width:100%;background-color:var(--pf-c-table--BackgroundColor)}@media screen and (max-width:1200px){.pf-c-table{--pf-c-table-caption--PaddingRight:var(--pf-c-table-caption--xl--PaddingRight);--pf-c-table-caption--PaddingLeft:var(--pf-c-table-caption--xl--PaddingLeft)}}@media screen and (min-width:1200px){.pf-c-table{--pf-c-table--cell--first-last-child--PaddingRight:var(--pf-c-table--cell--first-last-child--xl--PaddingRight);--pf-c-table--cell--first-last-child--PaddingLeft:var(--pf-c-table--cell--first-last-child--xl--PaddingLeft);--pf-c-table--m-compact--cell--first-last-child--PaddingLeft:var(--pf-c-table--m-compact--cell--first-last-child--xl--PaddingLeft);--pf-c-table--m-compact--cell--first-last-child--PaddingRight:var(--pf-c-table--m-compact--cell--first-last-child--xl--PaddingRight)}}.pf-c-table.pf-m-fixed{table-layout:fixed}.pf-c-table.pf-m-sticky-header{position:relative}.pf-c-table.pf-m-sticky-header>thead>tr{border-bottom:0}.pf-c-table.pf-m-sticky-header>thead>tr>*{position:sticky;top:0;z-index:var(--pf-global--ZIndex--xs);background:var(--pf-c-table--BackgroundColor)}.pf-c-table.pf-m-sticky-header>thead>tr>:after{position:absolute;right:0;bottom:0;left:0;content:""}.pf-c-table.pf-m-sticky-header>thead>tr>:after,.pf-c-table tr:not(.pf-c-table__expandable-row){border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-c-table tr>*{--pf-hidden-visible--visible--Display:var(--pf-c-table--cell--hidden-visible--Display);position:relative;width:var(--pf-c-table--cell--Width);min-width:var(--pf-c-table--cell--MinWidth);max-width:var(--pf-c-table--cell--MaxWidth);padding:var(--pf-c-table--cell--PaddingTop) var(--pf-c-table--cell--PaddingRight) var(--pf-c-table--cell--PaddingBottom) var(--pf-c-table--cell--PaddingLeft);overflow:var(--pf-c-table--cell--Overflow);font-size:var(--pf-c-table--cell--FontSize);font-weight:var(--pf-c-table--cell--FontWeight);color:var(--pf-c-table--cell--Color);text-overflow:var(--pf-c-table--cell--TextOverflow);word-break:var(--pf-c-table--cell--WordBreak);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:var(--pf-c-table--cell--first-last-child--PaddingLeft)}.pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:var(--pf-c-table--cell--first-last-child--PaddingRight)}.pf-c-table tr>.pf-m-center{text-align:center}.pf-c-table tr>:empty{width:auto;min-width:0;padding:0}.pf-c-table tr>.pf-m-help{--pf-c-table--cell--MinWidth:var(--pf-c-table--cell--m-help--MinWidth)}.pf-c-table tr>.pf-m-favorite{--pf-c-table__button--Color:var(--pf-c-table--cell--m-favorite--Color);--pf-c-table__sort--MinWidth:fit-content;--pf-c-table--cell--MaxWidth:fit-content;--pf-c-table--cell--Overflow:visible}.pf-c-table caption{padding-top:var(--pf-c-table-caption--PaddingTop);padding-bottom:var(--pf-c-table-caption--PaddingBottom);padding-left:var(--pf-c-table-caption--PaddingLeft);font-size:var(--pf-c-table-caption--FontSize);color:var(--pf-c-table-caption--Color);text-align:left;background-color:var(--pf-c-table--BackgroundColor)}.pf-c-table thead{--pf-c-table--cell--FontSize:var(--pf-c-table--thead--cell--FontSize);--pf-c-table--cell--FontWeight:var(--pf-c-table--thead--cell--FontWeight);vertical-align:bottom}.pf-c-table tbody{--pf-c-table--cell--PaddingTop:var(--pf-c-table--tbody--cell--PaddingTop);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--tbody--cell--PaddingBottom)}.pf-c-table tbody>tr>*{overflow-wrap:break-word;vertical-align:baseline}.pf-c-table tbody>tr>:first-child:after{position:absolute;top:var(--pf-c-table__expandable-row--after--Top);bottom:var(--pf-c-table__expandable-row--after--Bottom);left:0;content:"";background-color:transparent;border-left:var(--pf-c-table__expandable-row--after--BorderLeftWidth) solid var(--pf-c-table__expandable-row--after--BorderColor)}.pf-c-table tbody .pf-c-table__check>input{margin-top:var(--pf-c-table__check--input--MarginTop);vertical-align:top}.pf-c-table .pf-c-table__compound-expansion-toggle,.pf-c-table .pf-c-table__compound-expansion-toggle:first-child,.pf-c-table .pf-c-table__compound-expansion-toggle:last-child{padding:0}.pf-c-table .pf-c-table__sort{min-width:var(--pf-c-table__sort--MinWidth)}.pf-c-table .pf-m-help{min-width:var(--pf-c-table--th--m-help--MinWidth)}.pf-c-table .pf-m-truncate,.pf-c-table thead{--pf-c-table--cell--MinWidth:var(--pf-c-table--m-truncate--cell--MinWidth);--pf-c-table--cell--MaxWidth:var(--pf-c-table--m-truncate--cell--MaxWidth);--pf-c-table--cell--Overflow:hidden;--pf-c-table--cell--TextOverflow:ellipsis;--pf-c-table--cell--WhiteSpace:nowrap}.pf-c-table .pf-m-wrap{--pf-c-table--cell--WhiteSpace:normal}.pf-c-table .pf-m-nowrap,.pf-c-table .pf-m-wrap{--pf-c-table--cell--MinWidth:0;--pf-c-table--cell--MaxWidth:none;--pf-c-table--cell--Overflow:visible;--pf-c-table--cell--TextOverflow:clip}.pf-c-table .pf-m-nowrap{--pf-c-table--cell--WhiteSpace:nowrap}.pf-c-table .pf-c-table__icon,.pf-c-table .pf-m-fit-content{--pf-c-table--cell--MinWidth:fit-content;--pf-c-table--cell--MaxWidth:fit-content;--pf-c-table--cell--Width:1%;--pf-c-table--cell--Overflow:visible;--pf-c-table--cell--TextOverflow:clip;--pf-c-table--cell--WhiteSpace:nowrap}.pf-c-table .pf-m-break-word{--pf-c-table--cell--WordBreak:break-word;--pf-c-table--cell--WhiteSpace:normal}.pf-c-table.pf-m-no-border-rows>tbody>tr{border-bottom:0}.pf-c-table.pf-m-no-border-rows>tbody>tr>:first-child:after{border-left:0}.pf-c-table.pf-m-no-border-rows>tbody:not(.pf-m-expanded) .pf-c-table__compound-expansion-toggle .pf-c-table__button:before{display:none}.pf-c-table.pf-m-no-border-rows>tbody.pf-m-expanded>.pf-c-table__control-row{border-bottom:var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor)}.pf-c-table.pf-m-no-border-rows>tbody .pf-c-table__control-row>.pf-c-table__compound-expansion-toggle:first-child>:before{border-left-width:0}.pf-c-table__text{--pf-c-table--cell--MaxWidth:100%;position:relative;display:block;width:var(--pf-c-table--cell--Width);min-width:var(--pf-c-table--cell--MinWidth);max-width:var(--pf-c-table--cell--MaxWidth);overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);word-break:var(--pf-c-table--cell--WordBreak);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-c-table__text.pf-m-truncate{--pf-c-table--cell--MinWidth:100%}.pf-c-table__text.pf-m-truncate>*{overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-c-table__button{position:static;width:100%;padding:var(--pf-c-table--cell--PaddingTop) var(--pf-c-table--cell--PaddingRight) var(--pf-c-table--cell--PaddingBottom) var(--pf-c-table--cell--PaddingLeft);font-size:inherit;font-weight:inherit;color:var(--pf-c-table__button--Color);text-align:left;white-space:inherit;user-select:text;background-color:var(--pf-c-table__button--BackgroundColor);border:0}.pf-c-table__button:before{position:absolute;top:0;right:0;bottom:0;left:0;cursor:pointer;content:""}.pf-c-table__button:hover{color:var(--pf-c-table__button--hover--Color)}.pf-c-table__button:focus{color:var(--pf-c-table__button--focus--Color)}.pf-c-table__button:active{color:var(--pf-c-table__button--active--Color)}.pf-c-table__compound-expansion-toggle .pf-c-table__text,.pf-c-table__sort .pf-c-table__text{display:block;width:auto;overflow:var(--pf-c-table--cell--Overflow);text-overflow:var(--pf-c-table--cell--TextOverflow);white-space:var(--pf-c-table--cell--WhiteSpace)}.pf-c-table__sort .pf-c-table__text{--pf-c-table--cell--MinWidth:0}.pf-c-table__button-content,.pf-c-table__column-help{display:inline-grid;align-items:end;justify-content:start;grid-template-columns:auto max-content}.pf-c-table__button-content .pf-c-table__text,.pf-c-table__column-help .pf-c-table__text{min-width:auto}.pf-c-table th.pf-m-nowrap .pf-c-table__button-content,.pf-c-table th.pf-m-nowrap .pf-c-table__column-help,.pf-c-table thead.pf-m-nowrap .pf-c-table__button-content,.pf-c-table thead.pf-m-nowrap .pf-c-table__column-help,.pf-c-table tr.pf-m-nowrap .pf-c-table__button-content,.pf-c-table tr.pf-m-nowrap .pf-c-table__column-help{grid-template-columns:min-content max-content}.pf-c-table th.pf-m-fit-content .pf-c-table__button-content,.pf-c-table th.pf-m-fit-content .pf-c-table__column-help,.pf-c-table thead.pf-m-fit-content .pf-c-table__button-content,.pf-c-table thead.pf-m-fit-content .pf-c-table__column-help,.pf-c-table tr.pf-m-fit-content .pf-c-table__button-content,.pf-c-table tr.pf-m-fit-content .pf-c-table__column-help{grid-template-columns:fit-content max-content}.pf-c-table th.pf-m-truncate .pf-c-table__button-content,.pf-c-table th.pf-m-truncate .pf-c-table__column-help,.pf-c-table th.pf-m-wrap .pf-c-table__button-content,.pf-c-table th.pf-m-wrap .pf-c-table__column-help,.pf-c-table thead.pf-m-truncate .pf-c-table__button-content,.pf-c-table thead.pf-m-truncate .pf-c-table__column-help,.pf-c-table thead.pf-m-wrap .pf-c-table__button-content,.pf-c-table thead.pf-m-wrap .pf-c-table__column-help,.pf-c-table tr.pf-m-truncate .pf-c-table__button-content,.pf-c-table tr.pf-m-truncate .pf-c-table__column-help,.pf-c-table tr.pf-m-wrap .pf-c-table__button-content,.pf-c-table tr.pf-m-wrap .pf-c-table__column-help{grid-template-columns:auto max-content}.pf-c-table .pf-c-table__action,.pf-c-table .pf-c-table__inline-edit-action,.pf-c-table .pf-c-table__toggle{--pf-c-table--cell--PaddingBottom:0}.pf-c-table .pf-c-table__action,.pf-c-table .pf-c-table__check,.pf-c-table .pf-c-table__favorite,.pf-c-table .pf-c-table__inline-edit-action,.pf-c-table .pf-c-table__toggle,.pf-c-table th.pf-m-favorite{--pf-c-table--cell--MinWidth:0;--pf-c-table--cell--Width:1%}.pf-c-table__toggle{--pf-c-table--cell--PaddingRight:0;--pf-c-table--cell--PaddingLeft:0;vertical-align:top}.pf-c-table__toggle .pf-c-button{margin-top:var(--pf-c-table__toggle--c-button--MarginTop)}.pf-c-table__toggle .pf-c-button.pf-m-expanded .pf-c-table__toggle-icon{transform:rotate(var(--pf-c-table__toggle--c-button--m-expanded__toggle-icon--Rotate))}.pf-c-table__toggle .pf-c-table__toggle-icon{transition:var(--pf-c-table__toggle--c-button__toggle-icon--Transition);transform:rotate(var(--pf-c-table__toggle--c-button__toggle-icon--Rotate))}.pf-c-table__toggle svg{pointer-events:none}.pf-c-table__check{--pf-c-table--cell--FontSize:var(--pf-c-table__check--input--FontSize)}.pf-c-table__favorite .pf-c-button{--pf-c-button--m-plain--Color:var(--pf-c-table__favorite--c-button--Color);--pf-c-button--FontSize:var(--pf-c-table__favorite--c-button--FontSize);margin:var(--pf-c-table__favorite--c-button--MarginTop) var(--pf-c-table__favorite--c-button--MarginRight) var(--pf-c-table__favorite--c-button--MarginBottom) var(--pf-c-table__favorite--c-button--MarginLeft)}.pf-m-favorited.pf-c-table__favorite .pf-c-button{--pf-c-button--m-plain--Color:var(--pf-c-table__favorite--m-favorited--c-button--Color)}.pf-c-table__action,.pf-c-table__inline-edit-action{--pf-c-table--cell--PaddingTop:0;--pf-c-table--cell--PaddingRight:var(--pf-c-table__action--PaddingRight);--pf-c-table--cell--PaddingBottom:0;--pf-c-table--cell--PaddingLeft:var(--pf-c-table__action--PaddingLeft);padding-top:0;padding-bottom:0;vertical-align:middle}.pf-c-table__inline-edit-action{--pf-c-table--cell--PaddingLeft:0;--pf-c-table--cell--PaddingRight:0;text-align:right}.pf-c-table__compound-expansion-toggle{--pf-c-table__button--Color:var(--pf-c-table__compound-expansion-toggle__button--Color);--pf-c-table__button--hover--Color:var(--pf-c-table__compound-expansion-toggle__button--hover--Color);--pf-c-table__button--focus--Color:var(--pf-c-table__compound-expansion-toggle__button--focus--Color);--pf-c-table__button--active--Color:var(--pf-c-table__compound-expansion-toggle__button--active--Color);position:relative}.pf-c-table__compound-expansion-toggle.pf-m-truncate{overflow:visible}.pf-c-table__compound-expansion-toggle .pf-c-table__button{min-width:100%;overflow:hidden}.pf-c-table__compound-expansion-toggle .pf-c-table__button:active,.pf-c-table__compound-expansion-toggle .pf-c-table__button:focus,.pf-c-table__compound-expansion-toggle .pf-c-table__button:hover{outline:0}.pf-c-table__compound-expansion-toggle .pf-c-table__button:after,.pf-c-table__compound-expansion-toggle .pf-c-table__button:before{position:absolute;right:0;content:"";border-style:solid;border-width:0}.pf-c-table__compound-expansion-toggle .pf-c-table__button:before{top:0;bottom:var(--pf-c-table__compound-expansion-toggle__button--before--Bottom);left:var(--pf-c-table__compound-expansion-toggle__button--before--Left);border-color:var(--pf-c-table__compound-expansion-toggle__button--before--BorderColor);border-right-width:var(--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth);border-left-width:var(--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth)}.pf-c-table__compound-expansion-toggle .pf-c-table__button:after{top:var(--pf-c-table__compound-expansion-toggle__button--after--Top);left:var(--pf-c-table__compound-expansion-toggle__button--after--Left);pointer-events:none;border-color:var(--pf-c-table__compound-expansion-toggle__button--after--BorderColor);border-top-width:var(--pf-c-table__compound-expansion-toggle__button--after--BorderTopWidth)}.pf-c-table__compound-expansion-toggle.pf-m-expanded,.pf-c-table__compound-expansion-toggle:focus-within,.pf-c-table__compound-expansion-toggle:hover{--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth:var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base);--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base);--pf-c-table__compound-expansion-toggle__button--after--BorderTopWidth:var(--pf-c-table__compound-expansion-toggle__button--after--border-width--base)}.pf-c-table__compound-expansion-toggle:first-child{--pf-c-table__compound-expansion-toggle__button--before--Left:0;--pf-c-table__compound-expansion-toggle__button--after--Left:0}.pf-c-table__compound-expansion-toggle.pf-m-expanded .pf-c-table__button:before{border-bottom:var(--pf-c-table--BackgroundColor) solid var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base)}.pf-c-table__compound-expansion-toggle.pf-m-expanded:first-child{--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth:0}.pf-c-table__compound-expansion-toggle:focus-within{outline-offset:var(--pf-c-table__button--OutlineOffset)}@media (-webkit-min-device-pixel-ratio:0){.pf-c-table__compound-expansion-toggle:focus-within{outline-style:auto;outline-color:-webkit-focus-ring-color}}.pf-c-table__column-help-action{margin-left:var(--pf-c-table__column-help--MarginLeft);transform:translateY(var(--pf-c-table__column-help--TranslateY))}.pf-c-table__column-help-action .pf-c-button{--pf-c-button--PaddingRight:var(--pf-c-table__column-help--c-button--PaddingRight);--pf-c-button--PaddingLeft:var(--pf-c-table__column-help--c-button--PaddingLeft);margin-top:var(--pf-c-table__column-help--c-button--MarginTop);margin-bottom:var(--pf-c-table__column-help--c-button--MarginBottom);font-size:inherit;line-height:1}.pf-c-table__sort .pf-c-table__button{--pf-c-table--cell--PaddingTop:var(--pf-c-table__sort__button--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table__sort__button--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table__sort__button--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table__sort__button--PaddingLeft);display:flex;width:auto;margin-top:var(--pf-c-table__sort__button--MarginTop);margin-bottom:var(--pf-c-table__sort__button--MarginBottom);margin-left:var(--pf-c-table__sort__button--MarginLeft)}.pf-c-table__sort .pf-c-table__button:hover{--pf-c-table__sort-indicator--Color:var(--pf-c-table__sort__button--hover__sort-indicator--Color);--pf-c-table__sort__button__text--Color:var(--pf-c-table__sort__button--hover__text--Color)}.pf-c-table__sort .pf-c-table__button:focus{--pf-c-table__sort-indicator--Color:var(--pf-c-table__sort__button--focus__sort-indicator--Color);--pf-c-table__sort__button__text--Color:var(--pf-c-table__sort__button--focus__text--Color)}.pf-c-table__sort .pf-c-table__button:active{--pf-c-table__sort-indicator--Color:var(--pf-c-table__sort__button--active__sort-indicator--Color);--pf-c-table__sort__button__text--Color:var(--pf-c-table__sort__button--active__text--Color)}.pf-c-table__sort .pf-c-table__button .pf-c-table__text{color:var(--pf-c-table__sort__button__text--Color)}.pf-c-table__sort.pf-m-selected .pf-c-table__button{--pf-c-table__sort-indicator--Color:var(--pf-c-table__sort--m-selected__sort-indicator--Color);--pf-c-table__sort__button__text--Color:var(--pf-c-table__sort--m-selected__button__text--Color);color:var(--pf-c-table__sort--m-selected__button--Color)}.pf-c-table__sort.pf-m-help{--pf-c-table--th--m-help--MinWidth:var(--pf-c-table__sort--m-help--MinWidth)}.pf-c-table__sort.pf-m-favorite{--pf-c-table__sort__button__text--Color:var(--pf-c-table__sort--m-favorite__button__text--Color);--pf-c-table__sort__button--hover__text--Color:var(--pf-c-table__sort--m-favorite__button--hover__text--Color);--pf-c-table__sort__button--focus__text--Color:var(--pf-c-table__sort--m-favorite__button--focus__text--Color);--pf-c-table__sort__button--active__text--Color:var(--pf-c-table__sort--m-favorite__button--active__text--Color);--pf-c-table__sort--m-selected__button__text--Color:currentColor}.pf-c-table__sort-indicator{grid-column:2;margin-left:var(--pf-c-table__sort-indicator--MarginLeft);color:var(--pf-c-table__sort-indicator--Color);pointer-events:none}.pf-c-table__expandable-row{--pf-c-table--cell--PaddingTop:0;--pf-c-table--cell--PaddingBottom:0;position:relative;border-bottom:0 solid transparent;box-shadow:0 0 0 0 transparent}.pf-c-table__expandable-row,.pf-c-table__expandable-row td:first-child:after{transition:var(--pf-c-table__expandable-row--Transition)}.pf-c-table__expandable-row td.pf-m-no-padding,.pf-c-table__expandable-row th.pf-m-no-padding{padding:0 0 0 var(--pf-c-table__expandable-row--after--border-width--base)}.pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,.pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content{padding:0}.pf-c-table__expandable-row .pf-c-table__expandable-row-content{padding-top:var(--pf-c-table__expandable-row-content--PaddingTop);padding-bottom:var(--pf-c-table__expandable-row-content--PaddingBottom)}.pf-c-table__expandable-row.pf-m-expanded{border-bottom-color:var(--pf-c-table__expandable-row--m-expanded--BorderBottomColor);border-bottom-width:var(--pf-c-table--border-width--base);box-shadow:var(--pf-c-table__expandable-row--m-expanded--BoxShadow)}.pf-c-table__expandable-row:not(.pf-m-expanded){display:none;visibility:hidden}.pf-c-table__compound-expansion-toggle.pf-m-expanded:first-child,.pf-c-table__expandable-row.pf-m-expanded>:first-child,.pf-c-table tbody.pf-m-expanded>tr>:not(.pf-c-table__compound-expansion-toggle){--pf-c-table__expandable-row--after--BorderLeftWidth:var(--pf-c-table__expandable-row--after--border-width--base)}.pf-c-table .pf-c-table tr>:first-child{--pf-c-table--cell--PaddingLeft:var(--pf-c-table--nested--first-last-child--PaddingLeft)}.pf-c-table .pf-c-table tr>:last-child{--pf-c-table--cell--PaddingRight:var(--pf-c-table--nested--first-last-child--PaddingRight)}.pf-c-table.pf-m-compact{--pf-c-table--cell--FontSize:var(--pf-c-table--m-compact--FontSize);--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-compact--cell--PaddingTop);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-compact--cell--PaddingRight);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-compact--cell--PaddingBottom);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-compact--cell--PaddingLeft)}.pf-c-table.pf-m-compact tr{--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-compact--cell--PaddingLeft);--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-compact--cell--PaddingRight)}.pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row){--pf-c-table--cell--FontSize:var(--pf-c-table--m-compact--FontSize);--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-compact--cell--PaddingTop);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-compact--cell--PaddingBottom)}.pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row)>:first-child{--pf-c-table--cell--PaddingLeft:var(--pf-c-table--m-compact--cell--first-last-child--PaddingLeft)}.pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row)>:last-child{--pf-c-table--cell--PaddingRight:var(--pf-c-table--m-compact--cell--first-last-child--PaddingRight)}.pf-c-table.pf-m-compact thead th{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-compact-th--PaddingTop);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-compact-th--PaddingBottom)}.pf-c-table.pf-m-compact .pf-c-table__action{--pf-c-table--cell--PaddingTop:var(--pf-c-table__action--PaddingTop);--pf-c-table--cell--PaddingBottom:var(--pf-c-table__action--PaddingBottom);--pf-c-table--cell--PaddingLeft:var(--pf-c-table__action--PaddingLeft)}.pf-c-table.pf-m-compact .pf-c-table__toggle{--pf-c-table--cell--PaddingTop:var(--pf-c-table--m-compact__toggle--PaddingTop);--pf-c-table--cell--PaddingBottom:var(--pf-c-table--m-compact__toggle--PaddingBottom)}.pf-c-table.pf-m-compact .pf-c-table__icon{width:auto;min-width:0;text-align:center}.pf-c-table .pf-c-table.pf-m-compact tr>:first-child{--pf-c-table--cell--PaddingLeft:var(--pf-c-table--nested--first-last-child--PaddingLeft)}.pf-c-table .pf-c-table.pf-m-compact tr>:last-child{--pf-c-table--cell--PaddingRight:var(--pf-c-table--nested--first-last-child--PaddingRight)}.pf-c-table.pf-m-compact .pf-c-table__expandable-row-content{--pf-c-table__expandable-row-content--PaddingTop:var(--pf-c-table--m-compact__expandable-row-content--PaddingTop);--pf-c-table__expandable-row-content--PaddingBottom:var(--pf-c-table--m-compact__expandable-row-content--PaddingBottom)}.pf-c-table__icon-inline{display:flex;align-items:center}.pf-c-table__icon-inline>:not(:last-child){margin-right:var(--pf-c-table__icon-inline--MarginRight)}.pf-c-table .pf-m-width-10{--pf-c-table--cell--Width:10%}.pf-c-table .pf-m-width-15{--pf-c-table--cell--Width:15%}.pf-c-table .pf-m-width-20{--pf-c-table--cell--Width:20%}.pf-c-table .pf-m-width-25{--pf-c-table--cell--Width:25%}.pf-c-table .pf-m-width-30{--pf-c-table--cell--Width:30%}.pf-c-table .pf-m-width-35{--pf-c-table--cell--Width:35%}.pf-c-table .pf-m-width-40{--pf-c-table--cell--Width:40%}.pf-c-table .pf-m-width-45{--pf-c-table--cell--Width:45%}.pf-c-table .pf-m-width-50{--pf-c-table--cell--Width:50%}.pf-c-table .pf-m-width-60{--pf-c-table--cell--Width:60%}.pf-c-table .pf-m-width-70{--pf-c-table--cell--Width:70%}.pf-c-table .pf-m-width-80{--pf-c-table--cell--Width:80%}.pf-c-table .pf-m-width-90{--pf-c-table--cell--Width:90%}.pf-c-table .pf-m-width-100{--pf-c-table--cell--Width:100%}.pf-c-tabs{--pf-c-tabs--inset:0;--pf-c-tabs--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-tabs--before--border-width--base:var(--pf-global--BorderWidth--sm);--pf-c-tabs--before--BorderTopWidth:0;--pf-c-tabs--before--BorderRightWidth:0;--pf-c-tabs--before--BorderBottomWidth:var(--pf-c-tabs--before--border-width--base);--pf-c-tabs--before--BorderLeftWidth:0;--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--MaxWidth:15.625rem;--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-box__item--m-current--first-child__link--before--BorderLeftWidth:var(--pf-c-tabs__link--before--border-width--base);--pf-c-tabs--m-box__item--m-current--last-child__link--before--BorderRightWidth:var(--pf-c-tabs--before--border-width--base);--pf-c-tabs--m-color-scheme--light-300__link--BackgroundColor:transparent;--pf-c-tabs--m-color-scheme--light-300__item--m-current__link--BackgroundColor:var(--pf-global--BackgroundColor--light-300);--pf-c-tabs__link--Color:var(--pf-global--Color--200);--pf-c-tabs__link--FontSize:var(--pf-global--FontSize--md);--pf-c-tabs__link--BackgroundColor:transparent;--pf-c-tabs__link--OutlineOffset:-0.375rem;--pf-c-tabs__link--PaddingTop:var(--pf-global--spacer--sm);--pf-c-tabs__link--PaddingRight:var(--pf-global--spacer--md);--pf-c-tabs__link--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-tabs__link--PaddingLeft:var(--pf-global--spacer--md);--pf-c-tabs__item--m-current__link--Color:var(--pf-global--Color--100);--pf-c-tabs__item--m-current__link--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-tabs--m-vertical__link--PaddingTop:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical__link--PaddingBottom:var(--pf-global--spacer--md);--pf-c-tabs--m-box__link--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-tabs--m-secondary__link--FontSize:var(--pf-global--FontSize--sm);--pf-c-tabs__link--before--border-color--base:var(--pf-global--BorderColor--100);--pf-c-tabs__link--before--BorderRightColor:var(--pf-c-tabs__link--before--border-color--base);--pf-c-tabs__link--before--BorderBottomColor:var(--pf-c-tabs__link--before--border-color--base);--pf-c-tabs__link--before--border-width--base:var(--pf-global--BorderWidth--sm);--pf-c-tabs__link--before--BorderTopWidth:0;--pf-c-tabs__link--before--BorderRightWidth:0;--pf-c-tabs__link--before--BorderBottomWidth:0;--pf-c-tabs__link--before--BorderLeftWidth:0;--pf-c-tabs__link--before--Left:calc(var(--pf-c-tabs__link--before--border-width--base)*-1);--pf-c-tabs__link--after--Top:auto;--pf-c-tabs__link--after--Right:0;--pf-c-tabs__link--after--Bottom:0;--pf-c-tabs__link--after--BorderColor:var(--pf-global--BorderColor--light-100);--pf-c-tabs__link--after--BorderWidth:0;--pf-c-tabs__link--after--BorderTopWidth:0;--pf-c-tabs__link--after--BorderRightWidth:0;--pf-c-tabs__link--after--BorderLeftWidth:0;--pf-c-tabs__link--hover--after--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-tabs__link--focus--after--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-tabs__link--active--after--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-tabs__item--m-current__link--after--BorderColor:var(--pf-global--active-color--100);--pf-c-tabs__item--m-current__link--after--BorderWidth:var(--pf-global--BorderWidth--lg);--pf-c-tabs__link--child--MarginRight:var(--pf-global--spacer--md);--pf-c-tabs__scroll-button--Color:var(--pf-global--Color--100);--pf-c-tabs__scroll-button--hover--Color:var(--pf-global--active-color--100);--pf-c-tabs__scroll-button--disabled--Color:var(--pf-global--disabled-color--200);--pf-c-tabs__scroll-button--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-tabs__scroll-button--Width:var(--pf-global--spacer--2xl);--pf-c-tabs__scroll-button--xl--Width:var(--pf-global--spacer--3xl);--pf-c-tabs__scroll-button--OutlineOffset:calc(-1*var(--pf-global--spacer--xs));--pf-c-tabs__scroll-button--TransitionDuration--margin:.125s;--pf-c-tabs__scroll-button--TransitionDuration--transform:.125s;--pf-c-tabs__scroll-button--TransitionDuration--opacity:.125s;--pf-c-tabs__scroll-button--before--BorderColor:var(--pf-c-tabs--before--BorderColor);--pf-c-tabs__scroll-button--before--border-width--base:var(--pf-global--BorderWidth--sm);--pf-c-tabs__scroll-button--before--BorderRightWidth:0;--pf-c-tabs__scroll-button--before--BorderBottomWidth:var(--pf-c-tabs__scroll-button--before--border-width--base);--pf-c-tabs__scroll-button--before--BorderLeftWidth:0;position:relative;display:flex;padding-right:var(--pf-c-tabs--inset);padding-left:var(--pf-c-tabs--inset);overflow:hidden}@media screen and (min-width:1200px){.pf-c-tabs{--pf-c-tabs__scroll-button--Width:var(--pf-c-tabs__scroll-button--xl--Width)}}.pf-c-tabs:before{position:absolute;right:0;bottom:0;left:0;border:solid var(--pf-c-tabs--before--BorderColor);border-width:var(--pf-c-tabs--before--BorderTopWidth) var(--pf-c-tabs--before--BorderRightWidth) var(--pf-c-tabs--before--BorderBottomWidth) var(--pf-c-tabs--before--BorderLeftWidth)}.pf-c-tabs.pf-m-fill .pf-c-tabs__list{flex-basis:100%}.pf-c-tabs.pf-m-fill .pf-c-tabs__item{flex-grow:1}.pf-c-tabs.pf-m-fill .pf-c-tabs__item:first-child{--pf-c-tabs--m-box__item--m-current--first-child__link--before--BorderLeftWidth:0}.pf-c-tabs.pf-m-fill .pf-c-tabs__item:last-child{--pf-c-tabs--m-box__item--m-current--last-child__link--before--BorderRightWidth:0}.pf-c-tabs.pf-m-fill .pf-c-tabs__link{flex-basis:100%;justify-content:center}.pf-c-tabs.pf-m-scrollable .pf-c-tabs__scroll-button{opacity:1}.pf-c-tabs.pf-m-scrollable .pf-c-tabs__scroll-button:first-of-type{margin-right:0;transform:translateX(0)}.pf-c-tabs.pf-m-scrollable .pf-c-tabs__scroll-button:nth-of-type(2){margin-left:0;transform:translateX(0)}.pf-c-tabs.pf-m-no-border-bottom,.pf-c-tabs.pf-m-secondary{--pf-c-tabs--before--BorderBottomWidth:0}.pf-c-tabs.pf-m-box .pf-c-tabs__link,.pf-c-tabs.pf-m-vertical .pf-c-tabs__link{--pf-c-tabs__link--after--BorderBottomWidth:0}.pf-c-tabs.pf-m-box{--pf-c-tabs__link--BackgroundColor:var(--pf-c-tabs--m-box__link--BackgroundColor);--pf-c-tabs__link--before--BorderBottomWidth:var(--pf-c-tabs__link--before--border-width--base);--pf-c-tabs__link--before--BorderRightWidth:var(--pf-c-tabs__link--before--border-width--base);--pf-c-tabs__link--after--Top:0;--pf-c-tabs__link--after--Bottom:auto}.pf-c-tabs.pf-m-box .pf-c-tabs__link{--pf-c-tabs__link--after--BorderTopWidth:var(--pf-c-tabs__link--after--BorderWidth)}.pf-c-tabs.pf-m-box .pf-c-tabs__item:last-child{--pf-c-tabs__link--before--BorderRightWidth:0}.pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current{--pf-c-tabs__link--BackgroundColor:var(--pf-c-tabs__item--m-current__link--BackgroundColor);--pf-c-tabs__link--before--BorderBottomColor:var(--pf-c-tabs__link--BackgroundColor)}.pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current:first-child .pf-c-tabs__link:before{border-left-width:var(--pf-c-tabs--m-box__item--m-current--first-child__link--before--BorderLeftWidth)}.pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current:last-child .pf-c-tabs__link:before{border-right-width:var(--pf-c-tabs--m-box__item--m-current--last-child__link--before--BorderRightWidth)}.pf-c-tabs.pf-m-box.pf-m-scrollable .pf-c-tabs__item.pf-m-current:first-child .pf-c-tabs__link:before,.pf-c-tabs.pf-m-box.pf-m-scrollable .pf-c-tabs__scroll-button:nth-of-type(2):before{left:calc(var(--pf-c-tabs__link--before--border-width--base)*-1)}.pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current+.pf-c-tabs__item{--pf-c-tabs__link--before--Left:0}.pf-c-tabs.pf-m-box.pf-m-color-scheme--light-300{--pf-c-tabs__link--BackgroundColor:var(--pf-c-tabs--m-color-scheme--light-300__link--BackgroundColor);--pf-c-tabs__item--m-current__link--BackgroundColor:var(--pf-c-tabs--m-color-scheme--light-300__item--m-current__link--BackgroundColor)}.pf-c-tabs.pf-m-vertical{--pf-c-tabs--inset:var(--pf-c-tabs--m-vertical--inset);--pf-c-tabs--before--BorderBottomWidth:0;--pf-c-tabs--before--BorderLeftWidth:var(--pf-c-tabs--before--border-width--base);--pf-c-tabs__link--PaddingTop:var(--pf-c-tabs--m-vertical__link--PaddingTop);--pf-c-tabs__link--PaddingBottom:var(--pf-c-tabs--m-vertical__link--PaddingBottom);--pf-c-tabs__link--before--Left:0;--pf-c-tabs__link--after--Top:0;--pf-c-tabs__link--after--Bottom:0;--pf-c-tabs__link--after--Right:auto;display:inline-flex;flex-direction:column;height:100%;padding:0}.pf-c-tabs.pf-m-vertical:before{top:0;right:auto}.pf-c-tabs.pf-m-vertical .pf-c-tabs__list{flex-direction:column;max-width:var(--pf-c-tabs--m-vertical--MaxWidth)}.pf-c-tabs.pf-m-vertical .pf-c-tabs__item:first-child{margin-top:var(--pf-c-tabs--inset)}.pf-c-tabs.pf-m-vertical .pf-c-tabs__item:last-child{margin-bottom:var(--pf-c-tabs--inset)}.pf-c-tabs.pf-m-vertical .pf-c-tabs__link{--pf-c-tabs__link--after--BorderTopWidth:0;--pf-c-tabs__link--after--BorderLeftWidth:var(--pf-c-tabs__link--after--BorderWidth);max-width:100%;text-align:left}.pf-c-tabs.pf-m-vertical .pf-c-tabs__item-text{max-width:100%;overflow-wrap:break-word}.pf-c-tabs.pf-m-box.pf-m-vertical{--pf-c-tabs--inset:var(--pf-c-tabs--m-vertical--m-box--inset);--pf-c-tabs--before--BorderLeftWidth:0;--pf-c-tabs--before--BorderRightWidth:var(--pf-c-tabs--before--border-width--base)}.pf-c-tabs.pf-m-box.pf-m-vertical:before{right:0;left:auto}.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item:last-child{--pf-c-tabs__link--before--BorderBottomWidth:0;--pf-c-tabs__link--before--BorderRightWidth:var(--pf-c-tabs__link--before--border-width--base)}.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item.pf-m-current{--pf-c-tabs__link--before--BorderRightColor:var(--pf-c-tabs__item--m-current__link--BackgroundColor);--pf-c-tabs__link--before--BorderBottomColor:var(--pf-c-tabs__link--before--border-color--base);--pf-c-tabs__link--before--BorderBottomWidth:var(--pf-c-tabs__link--before--border-width--base)}.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item.pf-m-current:first-child,.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item:first-child.pf-m-current{--pf-c-tabs__link--before--BorderTopWidth:var(--pf-c-tabs__link--before--border-width--base)}.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__link:after{top:calc(var(--pf-c-tabs__link--before--border-width--base)*-1)}.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item.pf-m-current+.pf-c-tabs__item .pf-c-tabs__link:after,.pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item:first-child .pf-c-tabs__link:after{top:0}.pf-c-tabs.pf-m-secondary{--pf-c-tabs__link--FontSize:var(--pf-c-tabs--m-secondary__link--FontSize)}.pf-c-tabs__list{scrollbar-width:none;-ms-overflow-style:-ms-autohiding-scrollbar;position:relative;display:flex;max-width:100%;overflow-x:auto;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.pf-c-tabs__list::-webkit-scrollbar{display:none}.pf-c-tabs__item{display:flex;flex:none}.pf-c-tabs__item.pf-m-current{--pf-c-tabs__link--Color:var(--pf-c-tabs__item--m-current__link--Color);--pf-c-tabs__link--after--BorderColor:var(--pf-c-tabs__item--m-current__link--after--BorderColor);--pf-c-tabs__link--after--BorderWidth:var(--pf-c-tabs__item--m-current__link--after--BorderWidth)}.pf-c-tabs__link,.pf-c-tabs__scroll-button{border:0}.pf-c-tabs:before,.pf-c-tabs__link:after,.pf-c-tabs__link:before,.pf-c-tabs__scroll-button:before{position:absolute;right:0;bottom:0;left:0;content:"";border-style:solid}.pf-c-tabs__link:after,.pf-c-tabs__link:before,.pf-c-tabs__scroll-button:before{top:0}.pf-c-tabs__link{--pf-c-tabs__link--after--BorderBottomWidth:var(--pf-c-tabs__link--after--BorderWidth);position:relative;display:flex;flex:1;padding:var(--pf-c-tabs__link--PaddingTop) var(--pf-c-tabs__link--PaddingRight) var(--pf-c-tabs__link--PaddingBottom) var(--pf-c-tabs__link--PaddingLeft);font-size:var(--pf-c-tabs__link--FontSize);color:var(--pf-c-tabs__link--Color);text-decoration:none;background-color:var(--pf-c-tabs__link--BackgroundColor);outline-offset:var(--pf-c-tabs__link--OutlineOffset)}.pf-c-tabs__link:before{pointer-events:none;border-bottom-color:var(--pf-c-tabs__link--before--border-color--base);border-right-color:var(--pf-c-tabs__link--before--border-color--base);border-width:var(--pf-c-tabs__link--before--BorderTopWidth) var(--pf-c-tabs__link--before--BorderRightWidth) var(--pf-c-tabs__link--before--BorderBottomWidth) var(--pf-c-tabs__link--before--BorderLeftWidth);border-color:var(--pf-c-tabs__link--before--border-color--base) var(--pf-c-tabs__link--before--BorderRightColor) var(--pf-c-tabs__link--before--BorderBottomColor) var(--pf-c-tabs__link--before--border-color--base)}.pf-c-tabs__link:after{top:var(--pf-c-tabs__link--after--Top);right:var(--pf-c-tabs__link--after--Right);bottom:var(--pf-c-tabs__link--after--Bottom);left:var(--pf-c-tabs__link--before--Left);border-color:var(--pf-c-tabs__link--after--BorderColor);border-width:var(--pf-c-tabs__link--after--BorderTopWidth) var(--pf-c-tabs__link--after--BorderRightWidth) var(--pf-c-tabs__link--after--BorderBottomWidth) var(--pf-c-tabs__link--after--BorderLeftWidth)}.pf-c-tabs__link:hover{--pf-c-tabs__link--after--BorderWidth:var(--pf-c-tabs__link--hover--after--BorderWidth)}.pf-c-tabs__link:focus{--pf-c-tabs__link--after--BorderWidth:var(--pf-c-tabs__link--focus--after--BorderWidth)}.pf-c-tabs__link:active{--pf-c-tabs__link--after--BorderWidth:var(--pf-c-tabs__link--active--after--BorderWidth)}.pf-c-tabs__link .pf-c-tabs__item-icon,.pf-c-tabs__link .pf-c-tabs__item-text{margin-right:var(--pf-c-tabs__link--child--MarginRight)}.pf-c-tabs__link .pf-c-tabs__item-icon:last-child,.pf-c-tabs__link .pf-c-tabs__item-text:last-child{--pf-c-tabs__link--child--MarginRight:0}.pf-c-tabs__scroll-button{flex:none;width:var(--pf-c-tabs__scroll-button--Width);line-height:1;color:var(--pf-c-tabs__scroll-button--Color);background-color:var(--pf-c-tabs__scroll-button--BackgroundColor);outline-offset:var(--pf-c-tabs__scroll-button--OutlineOffset);opacity:0;transition:margin var(--pf-c-tabs__scroll-button--TransitionDuration--margin),transform var(--pf-c-tabs__scroll-button--TransitionDuration--transform),opacity var(--pf-c-tabs__scroll-button--TransitionDuration--opacity)}.pf-c-tabs__scroll-button:active,.pf-c-tabs__scroll-button:focus,.pf-c-tabs__scroll-button:hover{--pf-c-tabs__scroll-button--Color:var(--pf-c-tabs__scroll-button--hover--Color)}.pf-c-tabs__scroll-button:before{border-color:var(--pf-c-tabs__scroll-button--before--BorderColor);border-left-width:var(--pf-c-tabs__scroll-button--before--BorderLeftWidth);border-bottom-width:var(--pf-c-tabs__scroll-button--before--BorderBottomWidth);border-right-width:var(--pf-c-tabs__scroll-button--before--BorderRightWidth);border-top-width:0}.pf-c-tabs__scroll-button:first-of-type{--pf-c-tabs__scroll-button--before--BorderRightWidth:var(--pf-c-tabs__scroll-button--before--border-width--base);margin-right:calc(var(--pf-c-tabs__scroll-button--Width)*-1);transform:translateX(-100%)}.pf-c-tabs__scroll-button:nth-of-type(2){--pf-c-tabs__scroll-button--before--BorderLeftWidth:var(--pf-c-tabs__scroll-button--before--border-width--base);margin-left:calc(var(--pf-c-tabs__scroll-button--Width)*-1);transform:translateX(100%)}.pf-c-tabs__scroll-button:disabled{--pf-c-tabs__scroll-button--Color:var(--pf-c-tabs__scroll-button--disabled--Color);pointer-events:none}.pf-c-tabs.pf-m-inset-none{--pf-c-tabs--inset:0;--pf-c-tabs--m-vertical--inset:0;--pf-c-tabs--m-vertical--m-box--inset:0}.pf-c-tabs.pf-m-inset-sm{--pf-c-tabs--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--sm)}.pf-c-tabs.pf-m-inset-md{--pf-c-tabs--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--md)}.pf-c-tabs.pf-m-inset-lg{--pf-c-tabs--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--lg)}.pf-c-tabs.pf-m-inset-xl{--pf-c-tabs--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl)}.pf-c-tabs.pf-m-inset-2xl{--pf-c-tabs--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--2xl)}@media (min-width:576px){.pf-c-tabs.pf-m-inset-none-on-sm{--pf-c-tabs--inset:0;--pf-c-tabs--m-vertical--inset:0;--pf-c-tabs--m-vertical--m-box--inset:0}.pf-c-tabs.pf-m-inset-sm-on-sm{--pf-c-tabs--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--sm)}.pf-c-tabs.pf-m-inset-md-on-sm{--pf-c-tabs--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--md)}.pf-c-tabs.pf-m-inset-lg-on-sm{--pf-c-tabs--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--lg)}.pf-c-tabs.pf-m-inset-xl-on-sm{--pf-c-tabs--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl)}.pf-c-tabs.pf-m-inset-2xl-on-sm{--pf-c-tabs--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--2xl)}}@media (min-width:768px){.pf-c-tabs.pf-m-inset-none-on-md{--pf-c-tabs--inset:0;--pf-c-tabs--m-vertical--inset:0;--pf-c-tabs--m-vertical--m-box--inset:0}.pf-c-tabs.pf-m-inset-sm-on-md{--pf-c-tabs--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--sm)}.pf-c-tabs.pf-m-inset-md-on-md{--pf-c-tabs--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--md)}.pf-c-tabs.pf-m-inset-lg-on-md{--pf-c-tabs--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--lg)}.pf-c-tabs.pf-m-inset-xl-on-md{--pf-c-tabs--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl)}.pf-c-tabs.pf-m-inset-2xl-on-md{--pf-c-tabs--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--2xl)}}@media (min-width:992px){.pf-c-tabs.pf-m-inset-none-on-lg{--pf-c-tabs--inset:0;--pf-c-tabs--m-vertical--inset:0;--pf-c-tabs--m-vertical--m-box--inset:0}.pf-c-tabs.pf-m-inset-sm-on-lg{--pf-c-tabs--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--sm)}.pf-c-tabs.pf-m-inset-md-on-lg{--pf-c-tabs--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--md)}.pf-c-tabs.pf-m-inset-lg-on-lg{--pf-c-tabs--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--lg)}.pf-c-tabs.pf-m-inset-xl-on-lg{--pf-c-tabs--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl)}.pf-c-tabs.pf-m-inset-2xl-on-lg{--pf-c-tabs--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--2xl)}}@media (min-width:1200px){.pf-c-tabs.pf-m-inset-none-on-xl{--pf-c-tabs--inset:0;--pf-c-tabs--m-vertical--inset:0;--pf-c-tabs--m-vertical--m-box--inset:0}.pf-c-tabs.pf-m-inset-sm-on-xl{--pf-c-tabs--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--sm)}.pf-c-tabs.pf-m-inset-md-on-xl{--pf-c-tabs--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--md)}.pf-c-tabs.pf-m-inset-lg-on-xl{--pf-c-tabs--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--lg)}.pf-c-tabs.pf-m-inset-xl-on-xl{--pf-c-tabs--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl)}.pf-c-tabs.pf-m-inset-2xl-on-xl{--pf-c-tabs--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--2xl)}}@media (min-width:1450px){.pf-c-tabs.pf-m-inset-none-on-2xl{--pf-c-tabs--inset:0;--pf-c-tabs--m-vertical--inset:0;--pf-c-tabs--m-vertical--m-box--inset:0}.pf-c-tabs.pf-m-inset-sm-on-2xl{--pf-c-tabs--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--sm);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--sm)}.pf-c-tabs.pf-m-inset-md-on-2xl{--pf-c-tabs--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--md);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--md)}.pf-c-tabs.pf-m-inset-lg-on-2xl{--pf-c-tabs--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--lg);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--lg)}.pf-c-tabs.pf-m-inset-xl-on-2xl{--pf-c-tabs--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--xl)}.pf-c-tabs.pf-m-inset-2xl-on-2xl{--pf-c-tabs--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--inset:var(--pf-global--spacer--2xl);--pf-c-tabs--m-vertical--m-box--inset:var(--pf-global--spacer--2xl)}}.pf-c-tile{--pf-c-tile--PaddingTop:var(--pf-global--spacer--lg);--pf-c-tile--PaddingRight:var(--pf-global--spacer--lg);--pf-c-tile--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-tile--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-tile--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-tile--before--BorderColor:var(--pf-global--BorderColor--100);--pf-c-tile--before--BorderWidth:var(--pf-global--BorderWidth--sm);--pf-c-tile--before--BorderRadius:var(--pf-global--BorderRadius--sm);--pf-c-tile--hover--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-tile--m-selected--before--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-tile--m-selected--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-tile--focus--before--BorderWidth:var(--pf-global--BorderWidth--md);--pf-c-tile--focus--before--BorderColor:var(--pf-global--primary-color--100);--pf-c-tile--m-disabled--BackgroundColor:var(--pf-global--disabled-color--300);--pf-c-tile__title--Color:var(--pf-global--Color--100);--pf-c-tile--hover__title--Color:var(--pf-global--primary-color--100);--pf-c-tile--m-selected__title--Color:var(--pf-global--primary-color--100);--pf-c-tile--focus__title--Color:var(--pf-global--primary-color--100);--pf-c-tile--m-disabled__title--Color:var(--pf-global--disabled-color--100);--pf-c-tile__icon--MarginRight:var(--pf-global--spacer--sm);--pf-c-tile__icon--FontSize:var(--pf-global--icon--FontSize--md);--pf-c-tile__icon--Color:var(--pf-global--Color--100);--pf-c-tile--hover__icon--Color:var(--pf-global--primary-color--100);--pf-c-tile--m-selected__icon--Color:var(--pf-global--primary-color--100);--pf-c-tile--m-disabled__icon--Color:var(--pf-global--disabled-color--100);--pf-c-tile--focus__icon--Color:var(--pf-global--primary-color--100);--pf-c-tile__header--m-stacked__icon--MarginBottom:var(--pf-global--spacer--xs);--pf-c-tile__header--m-stacked__icon--FontSize:var(--pf-global--icon--FontSize--lg);--pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize:var(--pf-global--icon--FontSize--xl);--pf-c-tile__body--Color:var(--pf-global--Color--100);--pf-c-tile__body--FontSize:var(--pf-global--FontSize--xs);--pf-c-tile--m-disabled__body--Color:var(--pf-global--disabled-color--100);position:relative;display:inline-grid;padding:var(--pf-c-tile--PaddingTop) var(--pf-c-tile--PaddingRight) var(--pf-c-tile--PaddingBottom) var(--pf-c-tile--PaddingLeft);text-align:center;cursor:pointer;background-color:var(--pf-c-tile--BackgroundColor);grid-template-rows:min-content}.pf-c-tile:before{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;content:"";border:var(--pf-c-tile--before--BorderWidth) solid var(--pf-c-tile--before--BorderColor);border-radius:var(--pf-c-tile--before--BorderRadius)}.pf-c-tile:hover{--pf-c-tile__title--Color:var(--pf-c-tile--hover__title--Color);--pf-c-tile__icon--Color:var(--pf-c-tile--hover__icon--Color);--pf-c-tile--before--BorderColor:var(--pf-c-tile--hover--before--BorderColor)}.pf-c-tile.pf-m-selected{--pf-c-tile__title--Color:var(--pf-c-tile--m-selected__title--Color);--pf-c-tile__icon--Color:var(--pf-c-tile--m-selected__icon--Color);--pf-c-tile--before--BorderWidth:var(--pf-c-tile--m-selected--before--BorderWidth);--pf-c-tile--before--BorderColor:var(--pf-c-tile--m-selected--before--BorderColor)}.pf-c-tile:focus{--pf-c-tile__title--Color:var(--pf-c-tile--focus__title--Color);--pf-c-tile__icon--Color:var(--pf-c-tile--focus__icon--Color);--pf-c-tile--before--BorderWidth:var(--pf-c-tile--focus--before--BorderWidth);--pf-c-tile--before--BorderColor:var(--pf-c-tile--focus--before--BorderColor)}.pf-c-tile.pf-m-disabled{--pf-c-tile--BackgroundColor:var(--pf-c-tile--m-disabled--BackgroundColor);--pf-c-tile__title--Color:var(--pf-c-tile--m-disabled__title--Color);--pf-c-tile__body--Color:var(--pf-c-tile--m-disabled__body--Color);--pf-c-tile--before--BorderWidth:0;--pf-c-tile__icon--Color:var(--pf-c-tile--m-disabled__icon--Color);pointer-events:none}.pf-c-tile.pf-m-display-lg .pf-c-tile__header.pf-m-stacked{--pf-c-tile__icon--FontSize:var(--pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize)}.pf-c-tile__header{display:flex;align-items:center;justify-content:center}.pf-c-tile__header.pf-m-stacked{--pf-c-tile__icon--MarginRight:0;--pf-c-tile__icon--FontSize:var(--pf-c-tile__header--m-stacked__icon--FontSize);flex-direction:column;justify-content:normal}.pf-c-tile__header.pf-m-stacked .pf-c-tile__icon{display:flex;align-items:center;justify-content:center;margin-bottom:var(--pf-c-tile__header--m-stacked__icon--MarginBottom)}.pf-c-tile__title{color:var(--pf-c-tile__title--Color)}.pf-c-tile__body{font-size:var(--pf-c-tile__body--FontSize);color:var(--pf-c-tile__body--Color)}.pf-c-tile__icon{margin-right:var(--pf-c-tile__icon--MarginRight);font-size:var(--pf-c-tile__icon--FontSize);color:var(--pf-c-tile__icon--Color)}.pf-c-title{--pf-c-title--FontFamily:var(--pf-global--FontFamily--heading--sans-serif);--pf-c-title--m-4xl--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-title--m-4xl--FontSize:var(--pf-global--FontSize--4xl);--pf-c-title--m-4xl--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-title--m-3xl--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-title--m-3xl--FontSize:var(--pf-global--FontSize--3xl);--pf-c-title--m-3xl--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-title--m-2xl--LineHeight:var(--pf-global--LineHeight--sm);--pf-c-title--m-2xl--FontSize:var(--pf-global--FontSize--2xl);--pf-c-title--m-2xl--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-title--m-xl--LineHeight:var(--pf-global--LineHeight--md);--pf-c-title--m-xl--FontSize:var(--pf-global--FontSize--xl);--pf-c-title--m-xl--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-title--m-lg--LineHeight:var(--pf-global--LineHeight--md);--pf-c-title--m-lg--FontSize:var(--pf-global--FontSize--lg);--pf-c-title--m-lg--FontWeight:var(--pf-global--FontWeight--normal);--pf-c-title--m-md--LineHeight:var(--pf-global--LineHeight--md);--pf-c-title--m-md--FontSize:var(--pf-global--FontSize--md);--pf-c-title--m-md--FontWeight:var(--pf-global--FontWeight--normal);font-family:var(--pf-c-title--FontFamily);word-break:break-word}.pf-c-title.pf-m-4xl{font-size:var(--pf-c-title--m-4xl--FontSize);font-weight:var(--pf-c-title--m-4xl--FontWeight);line-height:var(--pf-c-title--m-4xl--LineHeight)}.pf-c-title.pf-m-3xl{font-size:var(--pf-c-title--m-3xl--FontSize);font-weight:var(--pf-c-title--m-3xl--FontWeight);line-height:var(--pf-c-title--m-3xl--LineHeight)}.pf-c-title.pf-m-2xl{font-size:var(--pf-c-title--m-2xl--FontSize);font-weight:var(--pf-c-title--m-2xl--FontWeight);line-height:var(--pf-c-title--m-2xl--LineHeight)}.pf-c-title.pf-m-xl{font-size:var(--pf-c-title--m-xl--FontSize);font-weight:var(--pf-c-title--m-xl--FontWeight);line-height:var(--pf-c-title--m-xl--LineHeight)}.pf-c-title.pf-m-lg{font-size:var(--pf-c-title--m-lg--FontSize);font-weight:var(--pf-c-title--m-lg--FontWeight);line-height:var(--pf-c-title--m-lg--LineHeight)}.pf-c-title.pf-m-md{font-size:var(--pf-c-title--m-md--FontSize);font-weight:var(--pf-c-title--m-md--FontWeight);line-height:var(--pf-c-title--m-md--LineHeight)}.pf-m-overpass-font .pf-c-title{--pf-c-title--m-md--FontWeight:var(--pf-global--FontWeight--semi-bold);--pf-c-title--m-lg--FontWeight:var(--pf-global--FontWeight--semi-bold)}.pf-c-toggle-group{--pf-c-toggle-group__button--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-toggle-group__button--PaddingRight:var(--pf-global--spacer--md);--pf-c-toggle-group__button--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-toggle-group__button--PaddingLeft:var(--pf-global--spacer--md);--pf-c-toggle-group__button--FontSize:var(--pf-global--FontSize--sm);--pf-c-toggle-group__button--LineHeight:calc(var(--pf-global--FontSize--md)*var(--pf-global--LineHeight--md));--pf-c-toggle-group__button--Color:var(--pf-global--Color--100);--pf-c-toggle-group__button--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-toggle-group__button--hover--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-toggle-group__button--focus--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-toggle-group__button--disabled--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-toggle-group__button--disabled--Color:var(--pf-global--disabled-color--100);--pf-c-toggle-group__item--first-child__button--BorderTopLeftRadius:var(--pf-global--BorderRadius--sm);--pf-c-toggle-group__item--first-child__button--BorderBottomLeftRadius:var(--pf-global--BorderRadius--sm);--pf-c-toggle-group__item--last-child__button--BorderTopRightRadius:var(--pf-global--BorderRadius--sm);--pf-c-toggle-group__item--last-child__button--BorderBottomRightRadius:var(--pf-global--BorderRadius--sm);--pf-c-toggle-group__icon--text--MarginLeft:var(--pf-global--spacer--sm);--pf-c-toggle-group__button--m-light--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-toggle-group__button--m-selected--BackgroundColor:var(--pf-global--primary-color--100);--pf-c-toggle-group__button--m-selected--Color:var(--pf-global--Color--light-100);display:flex}.pf-c-toggle-group__item:first-child .pf-c-toggle-group__button{border-top-left-radius:var(--pf-c-toggle-group__item--first-child__button--BorderTopLeftRadius);border-bottom-left-radius:var(--pf-c-toggle-group__item--first-child__button--BorderBottomLeftRadius)}.pf-c-toggle-group__item:last-child .pf-c-toggle-group__button{border-top-right-radius:var(--pf-c-toggle-group__item--last-child__button--BorderTopRightRadius);border-bottom-right-radius:var(--pf-c-toggle-group__item--last-child__button--BorderBottomRightRadius)}.pf-c-toggle-group__button{display:inline-flex;padding:var(--pf-c-toggle-group__button--PaddingTop) var(--pf-c-toggle-group__button--PaddingRight) var(--pf-c-toggle-group__button--PaddingBottom) var(--pf-c-toggle-group__button--PaddingLeft);font-size:var(--pf-c-toggle-group__button--FontSize);line-height:var(--pf-c-toggle-group__button--LineHeight);color:var(--pf-c-toggle-group__button--Color);background-color:var(--pf-c-toggle-group__button--BackgroundColor);border:0}.pf-c-toggle-group__button.pf-m-light{--pf-c-toggle-group__button--BackgroundColor:var(--pf-c-toggle-group__button--m-light--BackgroundColor)}.pf-c-toggle-group__button:hover{--pf-c-toggle-group__button--BackgroundColor:var(--pf-c-toggle-group__button--hover--BackgroundColor);text-decoration:none}.pf-c-toggle-group__button:focus{--pf-c-toggle-group__button--BackgroundColor:var(--pf-c-toggle-group__button--focus--BackgroundColor)}.pf-c-toggle-group__button.pf-m-selected{--pf-c-toggle-group__button--BackgroundColor:var(--pf-c-toggle-group__button--m-selected--BackgroundColor);--pf-c-toggle-group__button--Color:var(--pf-c-toggle-group__button--m-selected--Color)}.pf-c-toggle-group__button.pf-m-disabled,.pf-c-toggle-group__button:disabled{--pf-c-toggle-group__button--BackgroundColor:var(--pf-c-toggle-group__button--disabled--BackgroundColor);--pf-c-toggle-group__button--Color:var(--pf-c-toggle-group__button--disabled--Color);pointer-events:none}.pf-c-toggle-group__icon+.pf-c-toggle-group__text,.pf-c-toggle-group__text+.pf-c-toggle-group__icon{margin-left:var(--pf-c-toggle-group__icon--text--MarginLeft)}.pf-c-tooltip{--pf-c-tooltip--MaxWidth:18.75rem;--pf-c-tooltip--BoxShadow:var(--pf-global--BoxShadow--md);--pf-c-tooltip__content--PaddingTop:var(--pf-global--spacer--sm);--pf-c-tooltip__content--PaddingRight:var(--pf-global--spacer--md);--pf-c-tooltip__content--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-tooltip__content--PaddingLeft:var(--pf-global--spacer--md);--pf-c-tooltip__content--Color:var(--pf-global--Color--light-100);--pf-c-tooltip__content--BackgroundColor:var(--pf-global--BackgroundColor--dark-100);--pf-c-tooltip__content--FontSize:var(--pf-global--FontSize--sm);--pf-c-tooltip__arrow--Width:var(--pf-global--arrow--width);--pf-c-tooltip__arrow--Height:var(--pf-global--arrow--width);--pf-c-tooltip__arrow--m-top--TranslateX:-50%;--pf-c-tooltip__arrow--m-top--TranslateY:50%;--pf-c-tooltip__arrow--m-top--Rotate:45deg;--pf-c-tooltip__arrow--m-right--TranslateX:-50%;--pf-c-tooltip__arrow--m-right--TranslateY:-50%;--pf-c-tooltip__arrow--m-right--Rotate:45deg;--pf-c-tooltip__arrow--m-bottom--TranslateX:-50%;--pf-c-tooltip__arrow--m-bottom--TranslateY:-50%;--pf-c-tooltip__arrow--m-bottom--Rotate:45deg;--pf-c-tooltip__arrow--m-left--TranslateX:50%;--pf-c-tooltip__arrow--m-left--TranslateY:-50%;--pf-c-tooltip__arrow--m-left--Rotate:45deg;position:relative;max-width:var(--pf-c-tooltip--MaxWidth);box-shadow:var(--pf-c-tooltip--BoxShadow)}.pf-c-tooltip.pf-m-top .pf-c-tooltip__arrow{bottom:0;left:50%;transform:translateX(var(--pf-c-tooltip__arrow--m-top--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-top--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-top--Rotate))}.pf-c-tooltip.pf-m-bottom .pf-c-tooltip__arrow{top:0;left:50%;transform:translateX(var(--pf-c-tooltip__arrow--m-bottom--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-bottom--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-bottom--Rotate))}.pf-c-tooltip.pf-m-left .pf-c-tooltip__arrow{top:50%;right:0;transform:translateX(var(--pf-c-tooltip__arrow--m-left--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-left--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-left--Rotate))}.pf-c-tooltip.pf-m-right .pf-c-tooltip__arrow{top:50%;left:0;transform:translateX(var(--pf-c-tooltip__arrow--m-right--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-right--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-right--Rotate))}.pf-c-tooltip__content{position:relative;padding:var(--pf-c-tooltip__content--PaddingTop) var(--pf-c-tooltip__content--PaddingRight) var(--pf-c-tooltip__content--PaddingBottom) var(--pf-c-tooltip__content--PaddingLeft);font-size:var(--pf-c-tooltip__content--FontSize);color:var(--pf-c-tooltip__content--Color);text-align:center;word-break:break-word;background-color:var(--pf-c-tooltip__content--BackgroundColor)}.pf-c-tooltip__content.pf-m-text-align-left{text-align:left}.pf-c-tooltip__arrow{position:absolute;width:var(--pf-c-tooltip__arrow--Width);height:var(--pf-c-tooltip__arrow--Height);pointer-events:none;background-color:var(--pf-c-tooltip__content--BackgroundColor)}.pf-c-touchspin{--pf-c-touchspin__unit--c-input-group--MarginLeft:var(--pf-global--spacer--sm);--pf-c-touchspin__icon--FontSize:var(--pf-global--FontSize--xs);--pf-c-touchspin--c-form-control--width-base:calc(var(--pf-global--spacer--sm)*2);--pf-c-touchspin--c-form-control--width-chars:4;--pf-c-touchspin--c-form-control--Width:calc(var(--pf-c-touchspin--c-form-control--width-base) + var(--pf-c-touchspin--c-form-control--width-chars)*1ch);display:inline-flex;align-items:center}.pf-c-touchspin .pf-c-form-control{display:inline-flex;width:var(--pf-c-touchspin--c-form-control--Width);text-align:right}.pf-c-input-group+.pf-c-touchspin__unit,.pf-c-touchspin__unit+.pf-c-input-group{margin-left:var(--pf-c-touchspin__unit--c-input-group--MarginLeft)}.pf-c-touchspin__icon{font-size:var(--pf-c-touchspin__icon--FontSize)}.pf-c-tree-view{--pf-c-tree-view--PaddingTop:var(--pf-global--spacer--sm);--pf-c-tree-view--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-tree-view__node--indent--base:calc(var(--pf-global--spacer--md)*2 + var(--pf-c-tree-view__node-toggle-icon--MinWidth));--pf-c-tree-view__node--nested-indent--base:calc(var(--pf-c-tree-view__node--indent--base) - var(--pf-global--spacer--md));--pf-c-tree-view__node--PaddingTop:var(--pf-global--spacer--sm);--pf-c-tree-view__node--PaddingRight:var(--pf-global--spacer--sm);--pf-c-tree-view__node--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-tree-view__node--PaddingLeft:0;--pf-c-tree-view__node--Color:var(--pf-global--Color--100);--pf-c-tree-view__node--m-current--Color:var(--pf-global--link--Color);--pf-c-tree-view__node--m-current--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-tree-view__node--hover--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-tree-view__node--focus--BackgroundColor:var(--pf-global--palette--black-200);--pf-c-tree-view__list-item__list-item__node-toggle--Top:var(--pf-c-tree-view__node--PaddingTop);--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft);--pf-c-tree-view__list-item__list-item__node-toggle--TranslateX:-100%;--pf-c-tree-view__node-toggle-icon--MinWidth:var(--pf-global--FontSize--md);--pf-c-tree-view__node-toggle-icon--Transition:var(--pf-global--Transition);--pf-c-tree-view__node-toggle-button--PaddingTop:var(--pf-global--spacer--form-element);--pf-c-tree-view__node-toggle-button--PaddingRight:var(--pf-global--spacer--md);--pf-c-tree-view__node-toggle-button--PaddingBottom:var(--pf-global--spacer--form-element);--pf-c-tree-view__node-toggle-button--PaddingLeft:var(--pf-global--spacer--md);--pf-c-tree-view__node-toggle-button--MarginTop:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-tree-view__node-toggle-button--MarginBottom:calc(var(--pf-global--spacer--form-element)*-1);--pf-c-tree-view__node-check--MarginRight:var(--pf-global--spacer--sm);--pf-c-tree-view__node-count--MarginLeft:var(--pf-global--spacer--sm);--pf-c-tree-view__node-count--c-badge--m-read--BackgroundColor:var(--pf-global--disabled-color--200);--pf-c-tree-view__search--PaddingTop:var(--pf-global--spacer--sm);--pf-c-tree-view__search--PaddingRight:var(--pf-global--spacer--sm);--pf-c-tree-view__search--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-tree-view__search--PaddingLeft:var(--pf-global--spacer--sm);--pf-c-tree-view__node-icon--PaddingRight:var(--pf-global--spacer--sm);--pf-c-tree-view__node-icon--Color:var(--pf-global--icon--Color--light);--pf-c-tree-view__list-item--m-expanded__node-toggle-icon--Rotate:90deg;--pf-c-tree-view__node-text--max-lines:1;--pf-c-tree-view__action--MarginLeft:var(--pf-global--spacer--md);--pf-c-tree-view__action--focus--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-tree-view__action--Color:var(--pf-global--icon--Color--light);--pf-c-tree-view__action--hover--Color:var(--pf-global--icon--Color--dark);--pf-c-tree-view__action--focus--Color:var(--pf-global--icon--Color--dark);padding-top:var(--pf-c-tree-view--PaddingTop);padding-bottom:var(--pf-c-tree-view--PaddingBottom)}.pf-c-tree-view__list-item.pf-m-expanded>.pf-c-tree-view__content>.pf-c-tree-view__node>.pf-c-tree-view__node-toggle>.pf-c-tree-view__node-toggle-icon{transform:rotate(var(--pf-c-tree-view__list-item--m-expanded__node-toggle-icon--Rotate));text-align:center}.pf-c-tree-view__node{position:relative;display:flex;flex:1 1;align-items:center;min-width:0;padding:var(--pf-c-tree-view__node--PaddingTop) var(--pf-c-tree-view__node--PaddingRight) var(--pf-c-tree-view__node--PaddingBottom) var(--pf-c-tree-view__node--PaddingLeft);color:var(--pf-c-tree-view__node--Color);text-align:left;cursor:pointer;border:0}.pf-c-tree-view__node.pf-m-current{--pf-c-tree-view__node--Color:var(--pf-c-tree-view__node--m-current--Color);font-weight:var(--pf-c-tree-view__node--m-current--FontWeight)}.pf-c-tree-view__node:focus{background-color:var(--pf-c-tree-view__node--focus--BackgroundColor)}.pf-c-tree-view__node .pf-c-tree-view__node-count{margin-left:var(--pf-c-tree-view__node-count--MarginLeft)}.pf-c-tree-view__node .pf-c-tree-view__node-count .pf-c-badge.pf-m-read{--pf-c-badge--m-read--BackgroundColor:var(--pf-c-tree-view__node-count--c-badge--m-read--BackgroundColor)}.pf-c-tree-view__node-toggle-icon{display:inline-block;min-width:var(--pf-c-tree-view__node-toggle-icon--MinWidth);transition:var(--pf-c-tree-view__node-toggle-icon--Transition)}.pf-c-tree-view__node-check{margin-right:var(--pf-c-tree-view__node-check--MarginRight)}.pf-c-tree-view__node-toggle{display:inline-flex;align-items:center;justify-content:center;padding:var(--pf-c-tree-view__node-toggle-button--PaddingTop) var(--pf-c-tree-view__node-toggle-button--PaddingRight) var(--pf-c-tree-view__node-toggle-button--PaddingBottom) var(--pf-c-tree-view__node-toggle-button--PaddingLeft);margin-top:var(--pf-c-tree-view__node-toggle-button--MarginTop);margin-bottom:var(--pf-c-tree-view__node-toggle-button--MarginBottom);border:0}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__node-toggle{position:absolute;top:var(--pf-c-tree-view__list-item__list-item__node-toggle--Top);left:var(--pf-c-tree-view__list-item__list-item__node-toggle--Left);transform:translateX(var(--pf-c-tree-view__list-item__list-item__node-toggle--TranslateX))}.pf-c-tree-view__node-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pf-c-tree-view__search{padding:var(--pf-c-tree-view__search--PaddingTop) var(--pf-c-tree-view__search--PaddingRight) var(--pf-c-tree-view__search--PaddingBottom) var(--pf-c-tree-view__search--PaddingLeft)}.pf-c-tree-view__node-icon{padding-right:var(--pf-c-tree-view__node-icon--PaddingRight);color:var(--pf-c-tree-view__node-icon--Color)}.pf-c-tree-view__content{display:flex;align-items:center}.pf-c-tree-view__content:focus-within,.pf-c-tree-view__content:hover{background-color:var(--pf-c-tree-view__node--hover--BackgroundColor)}.pf-c-tree-view__action{margin-left:var(--pf-c-tree-view__action--MarginLeft);color:var(--pf-c-tree-view__action--Color);border:0}.pf-c-tree-view__action:hover{--pf-c-tree-view__action--Color:var(--pf-c-tree-view__action--hover--Color)}.pf-c-tree-view__action:focus{--pf-c-tree-view__action--Color:var(--pf-c-tree-view__action--focus--Color);background-color:var(--pf-c-tree-view__action--focus--BackgroundColor)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*1 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*2 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*3 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*4 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*5 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*6 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*7 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*8 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*9 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item{--pf-c-tree-view__node--PaddingLeft:calc(var(--pf-c-tree-view__node--nested-indent--base)*10 + var(--pf-c-tree-view__node--indent--base));--pf-c-tree-view__list-item__list-item__node-toggle--Left:var(--pf-c-tree-view__node--PaddingLeft)}.pf-c-wizard{--pf-c-wizard--Height:100%;--pf-c-modal-box--c-wizard--FlexBasis:47.625rem;--pf-c-wizard__header--BackgroundColor:var(--pf-global--BackgroundColor--dark-100);--pf-c-wizard__header--ZIndex:var(--pf-global--ZIndex--md);--pf-c-wizard__header--PaddingTop:var(--pf-global--spacer--lg);--pf-c-wizard__header--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__header--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-wizard__header--PaddingLeft:var(--pf-global--spacer--md);--pf-c-wizard__header--lg--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__header--lg--PaddingLeft:var(--pf-global--spacer--md);--pf-c-wizard__header--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-wizard__header--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-wizard__close--Top:calc(var(--pf-global--spacer--lg) - var(--pf-global--spacer--form-element));--pf-c-wizard__close--Right:0;--pf-c-wizard__close--xl--Right:var(--pf-global--spacer--lg);--pf-c-wizard__close--FontSize:var(--pf-global--FontSize--xl);--pf-c-wizard__title--PaddingRight:var(--pf-global--spacer--2xl);--pf-c-wizard__description--PaddingTop:var(--pf-global--spacer--sm);--pf-c-wizard__description--Color:var(--pf-global--Color--light-200);--pf-c-wizard__nav-link--Color:var(--pf-global--Color--100);--pf-c-wizard__nav-link--TextDecoration:var(--pf-global--link--TextDecoration);--pf-c-wizard__nav-link--hover--Color:var(--pf-global--link--Color);--pf-c-wizard__nav-link--focus--Color:var(--pf-global--link--Color);--pf-c-wizard__nav-link--m-current--Color:var(--pf-global--link--Color);--pf-c-wizard__nav-link--m-current--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-wizard__nav-link--m-disabled--Color:var(--pf-global--Color--dark-200);--pf-c-wizard__nav-list__nav-list__nav-link--m-current--FontWeight:var(--pf-global--FontWeight--bold);--pf-c-wizard__nav-link--before--Width:1.5rem;--pf-c-wizard__nav-link--before--Height:1.5rem;--pf-c-wizard__nav-link--before--Top:0;--pf-c-wizard__nav-link--before--BackgroundColor:var(--pf-global--BackgroundColor--200);--pf-c-wizard__nav-link--before--BorderRadius:var(--pf-global--BorderRadius--lg);--pf-c-wizard__nav-link--before--Color:var(--pf-global--Color--100);--pf-c-wizard__nav-link--before--FontSize:var(--pf-global--FontSize--sm);--pf-c-wizard__nav-link--before--TranslateX:calc(-100% - var(--pf-global--spacer--sm));--pf-c-wizard__nav-link--m-current--before--BackgroundColor:var(--pf-global--active-color--100);--pf-c-wizard__nav-link--m-current--before--Color:var(--pf-global--Color--light-100);--pf-c-wizard__nav-link--m-disabled--before--BackgroundColor:transparent;--pf-c-wizard__nav-link--m-disabled--before--Color:var(--pf-global--Color--dark-200);--pf-c-wizard__toggle--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-wizard__toggle--ZIndex:var(--pf-global--ZIndex--md);--pf-c-wizard__toggle--BoxShadow:var(--pf-global--BoxShadow--md-bottom);--pf-c-wizard__toggle--PaddingTop:var(--pf-global--spacer--lg);--pf-c-wizard__toggle--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__toggle--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-wizard__toggle--PaddingLeft:calc(var(--pf-global--spacer--md) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));--pf-c-wizard__toggle--m-expanded--BorderBottomWidth:var(--pf-global--BorderWidth--sm);--pf-c-wizard__toggle--m-expanded--BorderBottomColor:var(--pf-global--BorderColor--100);--pf-c-wizard--m-in-page__toggle--xl--PaddingLeft:calc(var(--pf-global--spacer--xl) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));--pf-c-wizard__toggle-num--before--Top:0;--pf-c-wizard__toggle-list-item--not-last-child--MarginRight:var(--pf-global--spacer--sm);--pf-c-wizard__toggle-list-item--MarginBottom:var(--pf-global--spacer--xs);--pf-c-wizard__toggle-list--MarginRight:var(--pf-global--spacer--sm);--pf-c-wizard__toggle-list--MarginBottom:calc(var(--pf-c-wizard__toggle-list-item--MarginBottom)*-1);--pf-c-wizard__toggle-separator--MarginLeft:var(--pf-global--spacer--sm);--pf-c-wizard__toggle-separator--Color:var(--pf-global--BorderColor--200);--pf-c-wizard__toggle-icon--LineHeight:var(--pf-global--LineHeight--md);--pf-c-wizard__toggle--m-expanded__toggle-icon--Rotate:180deg;--pf-c-wizard__nav--ZIndex:var(--pf-global--ZIndex--sm);--pf-c-wizard__nav--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-wizard__nav--BoxShadow:var(--pf-global--BoxShadow--md-bottom);--pf-c-wizard__nav--Width:100%;--pf-c-wizard__nav--lg--Width:15.625rem;--pf-c-wizard__nav--lg--BorderRightWidth:var(--pf-global--BorderWidth--sm);--pf-c-wizard__nav--lg--BorderRightColor:var(--pf-global--BorderColor--100);--pf-c-wizard__nav-list--PaddingTop:var(--pf-global--spacer--lg);--pf-c-wizard__nav-list--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__nav-list--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-wizard__nav-list--PaddingLeft:calc(var(--pf-global--spacer--md) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));--pf-c-wizard__nav-list--lg--PaddingTop:var(--pf-global--spacer--md);--pf-c-wizard__nav-list--lg--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__nav-list--lg--PaddingBottom:var(--pf-global--spacer--md);--pf-c-wizard__nav-list--xl--PaddingTop:var(--pf-global--spacer--lg);--pf-c-wizard__nav-list--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-wizard__nav-list--xl--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-wizard__nav-list--xl--PaddingLeft:calc(var(--pf-global--spacer--lg) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));--pf-c-wizard__nav-list--nested--MarginLeft:var(--pf-global--spacer--md);--pf-c-wizard__nav-list--nested--MarginTop:var(--pf-global--spacer--md);--pf-c-wizard__nav-item--MarginTop:var(--pf-global--spacer--md);--pf-c-wizard__outer-wrap--BackgroundColor:var(--pf-global--BackgroundColor--100);--pf-c-wizard__outer-wrap--lg--PaddingLeft:var(--pf-c-wizard__nav--Width);--pf-c-wizard__main--ZIndex:var(--pf-global--ZIndex--xs);--pf-c-wizard__main-body--PaddingTop:var(--pf-global--spacer--md);--pf-c-wizard__main-body--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__main-body--PaddingBottom:var(--pf-global--spacer--md);--pf-c-wizard__main-body--PaddingLeft:var(--pf-global--spacer--md);--pf-c-wizard__main-body--xl--PaddingTop:var(--pf-global--spacer--lg);--pf-c-wizard__main-body--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-wizard__main-body--xl--PaddingBottom:var(--pf-global--spacer--lg);--pf-c-wizard__main-body--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-wizard__footer--PaddingTop:var(--pf-global--spacer--md);--pf-c-wizard__footer--PaddingRight:var(--pf-global--spacer--md);--pf-c-wizard__footer--PaddingBottom:var(--pf-global--spacer--sm);--pf-c-wizard__footer--PaddingLeft:var(--pf-global--spacer--md);--pf-c-wizard__footer--xl--PaddingTop:var(--pf-global--spacer--lg);--pf-c-wizard__footer--xl--PaddingRight:var(--pf-global--spacer--lg);--pf-c-wizard__footer--xl--PaddingBottom:var(--pf-global--spacer--md);--pf-c-wizard__footer--xl--PaddingLeft:var(--pf-global--spacer--lg);--pf-c-wizard__footer--child--MarginRight:var(--pf-global--spacer--md);--pf-c-wizard__footer--child--MarginBottom:var(--pf-global--spacer--sm);position:relative;display:flex;flex-direction:column;height:var(--pf-c-wizard--Height)}@media screen and (min-width:992px){.pf-c-wizard{--pf-c-wizard__header--PaddingRight:var(--pf-c-wizard__header--lg--PaddingRight);--pf-c-wizard__header--PaddingLeft:var(--pf-c-wizard__header--lg--PaddingLeft)}}@media screen and (min-width:1200px){.pf-c-wizard{--pf-c-wizard__header--PaddingRight:var(--pf-c-wizard__header--xl--PaddingRight);--pf-c-wizard__header--PaddingLeft:var(--pf-c-wizard__header--xl--PaddingLeft);--pf-c-wizard__close--Right:var(--pf-c-wizard__close--xl--Right)}}@media screen and (min-width:992px){.pf-c-wizard{--pf-c-wizard__nav--Width:var(--pf-c-wizard__nav--lg--Width);--pf-c-wizard__nav--BoxShadow:none;--pf-c-wizard__nav-list--PaddingTop:var(--pf-c-wizard__nav-list--lg--PaddingTop);--pf-c-wizard__nav-list--PaddingRight:var(--pf-c-wizard__nav-list--lg--PaddingRight);--pf-c-wizard__nav-list--PaddingBottom:var(--pf-c-wizard__nav-list--lg--PaddingBottom)}}@media screen and (min-width:1200px){.pf-c-wizard{--pf-c-wizard__nav-list--PaddingTop:var(--pf-c-wizard__nav-list--xl--PaddingTop);--pf-c-wizard__nav-list--PaddingRight:var(--pf-c-wizard__nav-list--xl--PaddingRight);--pf-c-wizard__nav-list--PaddingBottom:var(--pf-c-wizard__nav-list--xl--PaddingBottom);--pf-c-wizard__nav-list--PaddingLeft:var(--pf-c-wizard__nav-list--xl--PaddingLeft);--pf-c-wizard__main-body--PaddingTop:var(--pf-c-wizard__main-body--xl--PaddingTop);--pf-c-wizard__main-body--PaddingRight:var(--pf-c-wizard__main-body--xl--PaddingRight);--pf-c-wizard__main-body--PaddingBottom:var(--pf-c-wizard__main-body--xl--PaddingBottom);--pf-c-wizard__main-body--PaddingLeft:var(--pf-c-wizard__main-body--xl--PaddingLeft);--pf-c-wizard__footer--PaddingTop:var(--pf-c-wizard__footer--xl--PaddingTop);--pf-c-wizard__footer--PaddingRight:var(--pf-c-wizard__footer--xl--PaddingRight);--pf-c-wizard__footer--PaddingBottom:var(--pf-c-wizard__footer--xl--PaddingBottom);--pf-c-wizard__footer--PaddingLeft:var(--pf-c-wizard__footer--xl--PaddingLeft)}}.pf-c-modal-box .pf-c-wizard{flex:1 1 var(--pf-c-modal-box--c-wizard--FlexBasis);min-height:0}.pf-c-wizard>:not(.pf-c-wizard__outer-wrap){flex-shrink:0}.pf-c-wizard.pf-m-finished{--pf-c-wizard__outer-wrap--lg--PaddingLeft:0}.pf-c-wizard.pf-m-finished .pf-c-wizard__footer,.pf-c-wizard.pf-m-finished .pf-c-wizard__nav,.pf-c-wizard.pf-m-finished .pf-c-wizard__toggle{display:none;visibility:hidden}.pf-c-wizard__header{color:var(--pf-global--Color--100);position:relative;z-index:var(--pf-c-wizard__header--ZIndex);padding:var(--pf-c-wizard__header--PaddingTop) var(--pf-c-wizard__header--PaddingRight) var(--pf-c-wizard__header--PaddingBottom) var(--pf-c-wizard__header--PaddingLeft);background-color:var(--pf-c-wizard__header--BackgroundColor)}.pf-c-wizard__header .pf-c-wizard__close{position:absolute;top:var(--pf-c-wizard__close--Top);right:var(--pf-c-wizard__close--Right);font-size:var(--pf-c-wizard__close--FontSize)}.pf-c-wizard__title{padding-right:var(--pf-c-wizard__title--PaddingRight);word-wrap:break-word}.pf-c-wizard__description{display:none;padding-top:var(--pf-c-wizard__description--PaddingTop);color:var(--pf-c-wizard__description--Color);visibility:hidden}@media screen and (min-width:992px){.pf-c-wizard__description{display:block;visibility:visible}}.pf-c-wizard__toggle{position:relative;z-index:var(--pf-c-wizard__toggle--ZIndex);display:flex;justify-content:space-between;width:100%;padding:var(--pf-c-wizard__toggle--PaddingTop) var(--pf-c-wizard__toggle--PaddingRight) var(--pf-c-wizard__toggle--PaddingBottom) var(--pf-c-wizard__toggle--PaddingLeft);background-color:var(--pf-c-wizard__toggle--BackgroundColor);border:0;box-shadow:var(--pf-c-wizard__toggle--BoxShadow)}@media screen and (min-width:992px){.pf-c-wizard__toggle{display:none;visibility:hidden}}.pf-c-wizard__toggle.pf-m-expanded{--pf-c-wizard__toggle--BoxShadow:none;border-bottom:var(--pf-c-wizard__toggle--m-expanded--BorderBottomWidth) solid var(--pf-c-wizard__toggle--m-expanded--BorderBottomColor)}.pf-c-wizard__toggle.pf-m-expanded .pf-c-wizard__toggle-icon{transform:rotate(var(--pf-c-wizard__toggle--m-expanded__toggle-icon--Rotate))}.pf-c-wizard__toggle-list{position:relative;display:flex;flex-wrap:wrap;align-items:baseline;margin-right:var(--pf-c-wizard__toggle-list--MarginRight);margin-bottom:var(--pf-c-wizard__toggle-list--MarginBottom);list-style:none}.pf-c-wizard__toggle-list-item{margin-bottom:var(--pf-c-wizard__toggle-list-item--MarginBottom);text-align:left;word-break:break-word}.pf-c-wizard__toggle-list-item:not(:last-child){margin-right:var(--pf-c-wizard__toggle-list-item--not-last-child--MarginRight)}.pf-c-wizard__toggle-num{--pf-c-wizard__nav-link--before--Top:var(--pf-c-wizard__toggle-num--before--Top)}.pf-c-wizard__toggle-separator{margin-left:var(--pf-c-wizard__toggle-separator--MarginLeft);color:var(--pf-c-wizard__toggle-separator--Color)}.pf-c-wizard__toggle-icon{line-height:var(--pf-c-wizard__toggle-icon--LineHeight)}.pf-c-wizard__outer-wrap{position:relative;display:flex;flex-direction:column;flex-grow:1;min-height:0;background-color:var(--pf-c-wizard__outer-wrap--BackgroundColor)}@media screen and (min-width:992px){.pf-c-wizard__outer-wrap{padding-left:var(--pf-c-wizard__outer-wrap--lg--PaddingLeft)}}.pf-c-wizard__inner-wrap{position:relative;display:flex;flex-direction:column;flex-grow:1;min-height:0}@media screen and (min-width:992px){.pf-c-wizard__inner-wrap{position:static}}.pf-c-wizard__nav{position:absolute;top:0;left:0;z-index:var(--pf-c-wizard__nav--ZIndex);display:none;width:var(--pf-c-wizard__nav--Width);max-height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;visibility:hidden;background-color:var(--pf-c-wizard__nav--BackgroundColor);box-shadow:var(--pf-c-wizard__nav--BoxShadow)}.pf-c-wizard__nav.pf-m-expanded{display:block;visibility:visible}@media screen and (min-width:992px){.pf-c-wizard__nav{display:block;height:100%;visibility:visible;border-right:var(--pf-c-wizard__nav--lg--BorderRightWidth) solid var(--pf-c-wizard__nav--lg--BorderRightColor)}}.pf-c-wizard__nav-list{padding:var(--pf-c-wizard__nav-list--PaddingTop) var(--pf-c-wizard__nav-list--PaddingRight) var(--pf-c-wizard__nav-list--PaddingBottom) var(--pf-c-wizard__nav-list--PaddingLeft);list-style:none;counter-reset:wizard-nav-count}.pf-c-wizard__nav-list .pf-c-wizard__nav-list{padding:0;margin-top:var(--pf-c-wizard__nav-list--nested--MarginTop);margin-left:var(--pf-c-wizard__nav-list--nested--MarginLeft)}.pf-c-wizard__nav-list .pf-c-wizard__nav-list .pf-c-wizard__nav-link:before{content:none}.pf-c-wizard__nav-list .pf-c-wizard__nav-list .pf-c-wizard__nav-link.pf-m-current{font-weight:var(--pf-c-wizard__nav-list__nav-list__nav-link--m-current--FontWeight)}.pf-c-wizard__nav-item+.pf-c-wizard__nav-item{margin-top:var(--pf-c-wizard__nav-item--MarginTop)}.pf-c-wizard__nav-link{position:relative;display:inline-block;color:var(--pf-c-wizard__nav-link--Color);text-align:left;text-decoration:var(--pf-c-wizard__nav-link--TextDecoration);word-break:break-word;border:0}.pf-c-wizard__nav-link:before,.pf-c-wizard__toggle-num{position:absolute;top:var(--pf-c-wizard__nav-link--before--Top);left:0;display:inline-flex;align-items:center;justify-content:center;width:var(--pf-c-wizard__nav-link--before--Width);height:var(--pf-c-wizard__nav-link--before--Height);font-size:var(--pf-c-wizard__nav-link--before--FontSize);line-height:1;color:var(--pf-c-wizard__nav-link--before--Color);background-color:var(--pf-c-wizard__nav-link--before--BackgroundColor);border-radius:var(--pf-c-wizard__nav-link--before--BorderRadius);transform:translateX(var(--pf-c-wizard__nav-link--before--TranslateX))}.pf-c-wizard__nav-link:before{top:0;content:counter(wizard-nav-count);counter-increment:wizard-nav-count}.pf-c-wizard__nav-link:hover{--pf-c-wizard__nav-link--Color:var(--pf-c-wizard__nav-link--hover--Color)}.pf-c-wizard__nav-link:focus{--pf-c-wizard__nav-link--Color:var(--pf-c-wizard__nav-link--focus--Color)}.pf-c-wizard__nav-link.pf-m-current{--pf-c-wizard__nav-link--Color:var(--pf-c-wizard__nav-link--m-current--Color);font-weight:var(--pf-c-wizard__nav-link--m-current--FontWeight)}.pf-c-wizard__nav-link.pf-m-current:before,.pf-c-wizard__toggle-num{--pf-c-wizard__nav-link--before--BackgroundColor:var(--pf-c-wizard__nav-link--m-current--before--BackgroundColor);--pf-c-wizard__nav-link--before--Color:var(--pf-c-wizard__nav-link--m-current--before--Color)}.pf-c-wizard__nav-link.pf-m-disabled,.pf-c-wizard__nav-link:disabled{--pf-c-wizard__nav-link--Color:var(--pf-c-wizard__nav-link--m-disabled--Color);pointer-events:none}.pf-c-wizard__nav-link.pf-m-disabled:before,.pf-c-wizard__nav-link:disabled:before{--pf-c-wizard__nav-link--before--BackgroundColor:var(--pf-c-wizard__nav-link--m-disabled--before--BackgroundColor);--pf-c-wizard__nav-link--before--Color:var(--pf-c-wizard__nav-link--m-disabled--before--Color)}.pf-c-wizard__main{z-index:var(--pf-c-wizard__main--ZIndex);flex:1 1 auto;overflow-x:hidden;overflow-y:auto;word-break:break-word}.pf-c-wizard__main-body{padding:var(--pf-c-wizard__main-body--PaddingTop) var(--pf-c-wizard__main-body--PaddingRight) var(--pf-c-wizard__main-body--PaddingBottom) var(--pf-c-wizard__main-body--PaddingLeft)}.pf-c-wizard__main-body.pf-m-no-padding{padding:0}.pf-c-wizard__footer{display:flex;flex-wrap:wrap;flex-shrink:0;padding:var(--pf-c-wizard__footer--PaddingTop) var(--pf-c-wizard__footer--PaddingRight) var(--pf-c-wizard__footer--PaddingBottom) var(--pf-c-wizard__footer--PaddingLeft)}.pf-c-wizard__footer>*{margin-bottom:var(--pf-c-wizard__footer--child--MarginBottom)}.pf-c-wizard__footer>:not(:last-child){margin-right:var(--pf-c-wizard__footer--child--MarginRight)}.pf-l-bullseye{--pf-l-bullseye--Padding:0;display:flex;align-items:center;justify-content:center;height:100%;padding:var(--pf-l-bullseye--Padding);margin:0}.pf-l-flex{--pf-l-flex--Display:flex;--pf-l-flex--FlexWrap:wrap;--pf-l-flex--AlignItems:baseline;--pf-l-flex--m-row--AlignItems:baseline;--pf-l-flex--m-row-reverse--AlignItems:baseline;--pf-l-flex--item--Order:0;--pf-l-flex--spacer-base:var(--pf-global--spacer--md);--pf-l-flex--spacer:var(--pf-l-flex--spacer-base);--pf-l-flex--spacer--none:0;--pf-l-flex--spacer--xs:var(--pf-global--spacer--xs);--pf-l-flex--spacer--sm:var(--pf-global--spacer--sm);--pf-l-flex--spacer--md:var(--pf-global--spacer--md);--pf-l-flex--spacer--lg:var(--pf-global--spacer--lg);--pf-l-flex--spacer--xl:var(--pf-global--spacer--xl);--pf-l-flex--spacer--2xl:var(--pf-global--spacer--2xl);--pf-l-flex--spacer--3xl:var(--pf-global--spacer--3xl);--pf-l-flex--spacer--4xl:var(--pf-global--spacer--4xl);display:var(--pf-l-flex--Display);flex-wrap:var(--pf-l-flex--FlexWrap);align-items:var(--pf-l-flex--AlignItems)}.pf-l-flex:last-child{--pf-l-flex--spacer:0}.pf-l-flex>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer-base);order:var(--pf-l-flex--item--Order);max-width:100%;margin-right:var(--pf-l-flex--spacer)}@media screen and (min-width:576px){.pf-l-flex>*{order:var(--pf-l-flex--item--Order-on-sm,var(--pf-l-flex--item--Order))}}@media screen and (min-width:768px){.pf-l-flex>*{order:var(--pf-l-flex--item--Order-on-md,var(--pf-l-flex--item--Order-on-sm,var(--pf-l-flex--item--Order)))}}@media screen and (min-width:992px){.pf-l-flex>*{order:var(--pf-l-flex--item--Order-on-lg,var(--pf-l-flex--item--Order-on-md,var(--pf-l-flex--item--Order-on-sm,var(--pf-l-flex--item--Order))))}}@media screen and (min-width:1200px){.pf-l-flex>*{order:var(--pf-l-flex--item--Order-on-xl,var(--pf-l-flex--item--Order-on-lg,var(--pf-l-flex--item--Order-on-md,var(--pf-l-flex--item--Order-on-sm,var(--pf-l-flex--item--Order)))))}}@media screen and (min-width:1450px){.pf-l-flex>*{order:var(--pf-l-flex--item--Order-on-2xl,var(--pf-l-flex--item--Order-on-xl,var(--pf-l-flex--item--Order-on-lg,var(--pf-l-flex--item--Order-on-md,var(--pf-l-flex--item--Order-on-sm,var(--pf-l-flex--item--Order))))))}}.pf-l-flex>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-flex{display:var(--pf-l-flex--Display)}.pf-l-flex.pf-m-inline-flex{--pf-l-flex--Display:inline-flex}.pf-l-flex.pf-m-column{flex-direction:column;align-items:normal}.pf-l-flex.pf-m-column>*{margin:0 0 var(--pf-l-flex--spacer) 0}.pf-l-flex.pf-m-column-reverse{flex-direction:column-reverse;align-items:normal}.pf-l-flex.pf-m-column-reverse>*{margin:var(--pf-l-flex--spacer) 0 0 0}.pf-l-flex.pf-m-row{flex-direction:row;align-items:var(--pf-l-flex--m-row--AlignItems)}.pf-l-flex.pf-m-row>*{margin:0 var(--pf-l-flex--spacer) 0 0}.pf-l-flex.pf-m-row-reverse{flex-direction:row-reverse;align-items:var(--pf-l-flex--m-row-reverse--AlignItems)}.pf-l-flex.pf-m-row-reverse>*{margin:0 0 0 var(--pf-l-flex--spacer)}.pf-l-flex.pf-m-wrap{flex-wrap:wrap}.pf-l-flex.pf-m-wrap-reverse{flex-wrap:wrap-reverse}.pf-l-flex.pf-m-nowrap{flex-wrap:nowrap}.pf-l-flex.pf-m-justify-content-flex-start{justify-content:flex-start}.pf-l-flex.pf-m-justify-content-flex-end{justify-content:flex-end}.pf-l-flex.pf-m-justify-content-center{justify-content:center}.pf-l-flex.pf-m-justify-content-space-between{justify-content:space-between}.pf-l-flex.pf-m-justify-content-space-around{justify-content:space-around}.pf-l-flex.pf-m-justify-content-space-evenly{justify-content:space-evenly}.pf-l-flex.pf-m-align-items-flex-start{align-items:flex-start}.pf-l-flex.pf-m-align-items-flex-end{align-items:flex-end}.pf-l-flex.pf-m-align-items-center{align-items:center}.pf-l-flex.pf-m-align-items-stretch{align-items:stretch}.pf-l-flex.pf-m-align-items-baseline{align-items:baseline}.pf-l-flex.pf-m-align-content-flex-start{align-content:flex-start}.pf-l-flex.pf-m-align-content-flex-end{align-content:flex-end}.pf-l-flex.pf-m-align-content-center{align-content:center}.pf-l-flex.pf-m-align-content-stretch{align-content:stretch}.pf-l-flex.pf-m-align-content-space-between{align-content:space-between}.pf-l-flex.pf-m-align-content-space-around{align-content:space-around}.pf-l-flex>.pf-m-align-right{margin-left:auto}.pf-l-flex>.pf-m-align-left{margin-left:0}.pf-l-flex>.pf-m-grow{flex-grow:1}.pf-l-flex>.pf-m-shrink{flex-shrink:1}.pf-l-flex>.pf-m-full-width{width:100%;margin-right:0}.pf-l-flex>.pf-m-flex-1{flex:1 0 0}.pf-l-flex>.pf-m-flex-2{flex:2 0 0}.pf-l-flex>.pf-m-flex-3{flex:3 0 0}.pf-l-flex>.pf-m-flex-4{flex:4 0 0}.pf-l-flex>.pf-m-flex-default{flex:0 1 auto}.pf-l-flex>.pf-m-flex-none{flex:none}.pf-l-flex>.pf-m-align-self-flex-start{align-self:flex-start}.pf-l-flex>.pf-m-align-self-flex-end{align-self:flex-end}.pf-l-flex>.pf-m-align-self-center{align-self:center}.pf-l-flex>.pf-m-align-self-baseline{align-self:baseline}.pf-l-flex>.pf-m-align-self-stretch{align-self:stretch}@media (min-width:576px){.pf-l-flex.pf-m-flex-on-sm{display:var(--pf-l-flex--Display)}.pf-l-flex.pf-m-inline-flex-on-sm{--pf-l-flex--Display:inline-flex}.pf-l-flex.pf-m-column-on-sm{flex-direction:column;align-items:normal}.pf-l-flex.pf-m-column-on-sm>*{margin:0 0 var(--pf-l-flex--spacer) 0}.pf-l-flex.pf-m-column-reverse-on-sm{flex-direction:column-reverse;align-items:normal}.pf-l-flex.pf-m-column-reverse-on-sm>*{margin:var(--pf-l-flex--spacer) 0 0 0}.pf-l-flex.pf-m-row-on-sm{flex-direction:row;align-items:var(--pf-l-flex--m-row--AlignItems)}.pf-l-flex.pf-m-row-on-sm>*{margin:0 var(--pf-l-flex--spacer) 0 0}.pf-l-flex.pf-m-row-reverse-on-sm{flex-direction:row-reverse;align-items:var(--pf-l-flex--m-row-reverse--AlignItems)}.pf-l-flex.pf-m-row-reverse-on-sm>*{margin:0 0 0 var(--pf-l-flex--spacer)}.pf-l-flex.pf-m-wrap-on-sm{flex-wrap:wrap}.pf-l-flex.pf-m-wrap-reverse-on-sm{flex-wrap:wrap-reverse}.pf-l-flex.pf-m-nowrap-on-sm{flex-wrap:nowrap}.pf-l-flex.pf-m-justify-content-flex-start-on-sm{justify-content:flex-start}.pf-l-flex.pf-m-justify-content-flex-end-on-sm{justify-content:flex-end}.pf-l-flex.pf-m-justify-content-center-on-sm{justify-content:center}.pf-l-flex.pf-m-justify-content-space-between-on-sm{justify-content:space-between}.pf-l-flex.pf-m-justify-content-space-around-on-sm{justify-content:space-around}.pf-l-flex.pf-m-justify-content-space-evenly-on-sm{justify-content:space-evenly}.pf-l-flex.pf-m-align-items-flex-start-on-sm{align-items:flex-start}.pf-l-flex.pf-m-align-items-flex-end-on-sm{align-items:flex-end}.pf-l-flex.pf-m-align-items-center-on-sm{align-items:center}.pf-l-flex.pf-m-align-items-stretch-on-sm{align-items:stretch}.pf-l-flex.pf-m-align-items-baseline-on-sm{align-items:baseline}.pf-l-flex.pf-m-align-content-flex-start-on-sm{align-content:flex-start}.pf-l-flex.pf-m-align-content-flex-end-on-sm{align-content:flex-end}.pf-l-flex.pf-m-align-content-center-on-sm{align-content:center}.pf-l-flex.pf-m-align-content-stretch-on-sm{align-content:stretch}.pf-l-flex.pf-m-align-content-space-between-on-sm{align-content:space-between}.pf-l-flex.pf-m-align-content-space-around-on-sm{align-content:space-around}.pf-l-flex>.pf-m-align-right-on-sm{margin-left:auto}.pf-l-flex>.pf-m-align-left-on-sm{margin-left:0}.pf-l-flex>.pf-m-grow-on-sm{flex-grow:1}.pf-l-flex>.pf-m-shrink-on-sm{flex-shrink:1}.pf-l-flex>.pf-m-full-width-on-sm{width:100%;margin-right:0}.pf-l-flex>.pf-m-flex-1-on-sm{flex:1 0 0}.pf-l-flex>.pf-m-flex-2-on-sm{flex:2 0 0}.pf-l-flex>.pf-m-flex-3-on-sm{flex:3 0 0}.pf-l-flex>.pf-m-flex-4-on-sm{flex:4 0 0}.pf-l-flex>.pf-m-flex-default-on-sm{flex:0 1 auto}.pf-l-flex>.pf-m-flex-none-on-sm{flex:none}.pf-l-flex>.pf-m-align-self-flex-start-on-sm{align-self:flex-start}.pf-l-flex>.pf-m-align-self-flex-end-on-sm{align-self:flex-end}.pf-l-flex>.pf-m-align-self-center-on-sm{align-self:center}.pf-l-flex>.pf-m-align-self-baseline-on-sm{align-self:baseline}.pf-l-flex>.pf-m-align-self-stretch-on-sm{align-self:stretch}}@media (min-width:768px){.pf-l-flex.pf-m-flex-on-md{display:var(--pf-l-flex--Display)}.pf-l-flex.pf-m-inline-flex-on-md{--pf-l-flex--Display:inline-flex}.pf-l-flex.pf-m-column-on-md{flex-direction:column;align-items:normal}.pf-l-flex.pf-m-column-on-md>*{margin:0 0 var(--pf-l-flex--spacer) 0}.pf-l-flex.pf-m-column-reverse-on-md{flex-direction:column-reverse;align-items:normal}.pf-l-flex.pf-m-column-reverse-on-md>*{margin:var(--pf-l-flex--spacer) 0 0 0}.pf-l-flex.pf-m-row-on-md{flex-direction:row;align-items:var(--pf-l-flex--m-row--AlignItems)}.pf-l-flex.pf-m-row-on-md>*{margin:0 var(--pf-l-flex--spacer) 0 0}.pf-l-flex.pf-m-row-reverse-on-md{flex-direction:row-reverse;align-items:var(--pf-l-flex--m-row-reverse--AlignItems)}.pf-l-flex.pf-m-row-reverse-on-md>*{margin:0 0 0 var(--pf-l-flex--spacer)}.pf-l-flex.pf-m-wrap-on-md{flex-wrap:wrap}.pf-l-flex.pf-m-wrap-reverse-on-md{flex-wrap:wrap-reverse}.pf-l-flex.pf-m-nowrap-on-md{flex-wrap:nowrap}.pf-l-flex.pf-m-justify-content-flex-start-on-md{justify-content:flex-start}.pf-l-flex.pf-m-justify-content-flex-end-on-md{justify-content:flex-end}.pf-l-flex.pf-m-justify-content-center-on-md{justify-content:center}.pf-l-flex.pf-m-justify-content-space-between-on-md{justify-content:space-between}.pf-l-flex.pf-m-justify-content-space-around-on-md{justify-content:space-around}.pf-l-flex.pf-m-justify-content-space-evenly-on-md{justify-content:space-evenly}.pf-l-flex.pf-m-align-items-flex-start-on-md{align-items:flex-start}.pf-l-flex.pf-m-align-items-flex-end-on-md{align-items:flex-end}.pf-l-flex.pf-m-align-items-center-on-md{align-items:center}.pf-l-flex.pf-m-align-items-stretch-on-md{align-items:stretch}.pf-l-flex.pf-m-align-items-baseline-on-md{align-items:baseline}.pf-l-flex.pf-m-align-content-flex-start-on-md{align-content:flex-start}.pf-l-flex.pf-m-align-content-flex-end-on-md{align-content:flex-end}.pf-l-flex.pf-m-align-content-center-on-md{align-content:center}.pf-l-flex.pf-m-align-content-stretch-on-md{align-content:stretch}.pf-l-flex.pf-m-align-content-space-between-on-md{align-content:space-between}.pf-l-flex.pf-m-align-content-space-around-on-md{align-content:space-around}.pf-l-flex>.pf-m-align-right-on-md{margin-left:auto}.pf-l-flex>.pf-m-align-left-on-md{margin-left:0}.pf-l-flex>.pf-m-grow-on-md{flex-grow:1}.pf-l-flex>.pf-m-shrink-on-md{flex-shrink:1}.pf-l-flex>.pf-m-full-width-on-md{width:100%;margin-right:0}.pf-l-flex>.pf-m-flex-1-on-md{flex:1 0 0}.pf-l-flex>.pf-m-flex-2-on-md{flex:2 0 0}.pf-l-flex>.pf-m-flex-3-on-md{flex:3 0 0}.pf-l-flex>.pf-m-flex-4-on-md{flex:4 0 0}.pf-l-flex>.pf-m-flex-default-on-md{flex:0 1 auto}.pf-l-flex>.pf-m-flex-none-on-md{flex:none}.pf-l-flex>.pf-m-align-self-flex-start-on-md{align-self:flex-start}.pf-l-flex>.pf-m-align-self-flex-end-on-md{align-self:flex-end}.pf-l-flex>.pf-m-align-self-center-on-md{align-self:center}.pf-l-flex>.pf-m-align-self-baseline-on-md{align-self:baseline}.pf-l-flex>.pf-m-align-self-stretch-on-md{align-self:stretch}}@media (min-width:992px){.pf-l-flex.pf-m-flex-on-lg{display:var(--pf-l-flex--Display)}.pf-l-flex.pf-m-inline-flex-on-lg{--pf-l-flex--Display:inline-flex}.pf-l-flex.pf-m-column-on-lg{flex-direction:column;align-items:normal}.pf-l-flex.pf-m-column-on-lg>*{margin:0 0 var(--pf-l-flex--spacer) 0}.pf-l-flex.pf-m-column-reverse-on-lg{flex-direction:column-reverse;align-items:normal}.pf-l-flex.pf-m-column-reverse-on-lg>*{margin:var(--pf-l-flex--spacer) 0 0 0}.pf-l-flex.pf-m-row-on-lg{flex-direction:row;align-items:var(--pf-l-flex--m-row--AlignItems)}.pf-l-flex.pf-m-row-on-lg>*{margin:0 var(--pf-l-flex--spacer) 0 0}.pf-l-flex.pf-m-row-reverse-on-lg{flex-direction:row-reverse;align-items:var(--pf-l-flex--m-row-reverse--AlignItems)}.pf-l-flex.pf-m-row-reverse-on-lg>*{margin:0 0 0 var(--pf-l-flex--spacer)}.pf-l-flex.pf-m-wrap-on-lg{flex-wrap:wrap}.pf-l-flex.pf-m-wrap-reverse-on-lg{flex-wrap:wrap-reverse}.pf-l-flex.pf-m-nowrap-on-lg{flex-wrap:nowrap}.pf-l-flex.pf-m-justify-content-flex-start-on-lg{justify-content:flex-start}.pf-l-flex.pf-m-justify-content-flex-end-on-lg{justify-content:flex-end}.pf-l-flex.pf-m-justify-content-center-on-lg{justify-content:center}.pf-l-flex.pf-m-justify-content-space-between-on-lg{justify-content:space-between}.pf-l-flex.pf-m-justify-content-space-around-on-lg{justify-content:space-around}.pf-l-flex.pf-m-justify-content-space-evenly-on-lg{justify-content:space-evenly}.pf-l-flex.pf-m-align-items-flex-start-on-lg{align-items:flex-start}.pf-l-flex.pf-m-align-items-flex-end-on-lg{align-items:flex-end}.pf-l-flex.pf-m-align-items-center-on-lg{align-items:center}.pf-l-flex.pf-m-align-items-stretch-on-lg{align-items:stretch}.pf-l-flex.pf-m-align-items-baseline-on-lg{align-items:baseline}.pf-l-flex.pf-m-align-content-flex-start-on-lg{align-content:flex-start}.pf-l-flex.pf-m-align-content-flex-end-on-lg{align-content:flex-end}.pf-l-flex.pf-m-align-content-center-on-lg{align-content:center}.pf-l-flex.pf-m-align-content-stretch-on-lg{align-content:stretch}.pf-l-flex.pf-m-align-content-space-between-on-lg{align-content:space-between}.pf-l-flex.pf-m-align-content-space-around-on-lg{align-content:space-around}.pf-l-flex>.pf-m-align-right-on-lg{margin-left:auto}.pf-l-flex>.pf-m-align-left-on-lg{margin-left:0}.pf-l-flex>.pf-m-grow-on-lg{flex-grow:1}.pf-l-flex>.pf-m-shrink-on-lg{flex-shrink:1}.pf-l-flex>.pf-m-full-width-on-lg{width:100%;margin-right:0}.pf-l-flex>.pf-m-flex-1-on-lg{flex:1 0 0}.pf-l-flex>.pf-m-flex-2-on-lg{flex:2 0 0}.pf-l-flex>.pf-m-flex-3-on-lg{flex:3 0 0}.pf-l-flex>.pf-m-flex-4-on-lg{flex:4 0 0}.pf-l-flex>.pf-m-flex-default-on-lg{flex:0 1 auto}.pf-l-flex>.pf-m-flex-none-on-lg{flex:none}.pf-l-flex>.pf-m-align-self-flex-start-on-lg{align-self:flex-start}.pf-l-flex>.pf-m-align-self-flex-end-on-lg{align-self:flex-end}.pf-l-flex>.pf-m-align-self-center-on-lg{align-self:center}.pf-l-flex>.pf-m-align-self-baseline-on-lg{align-self:baseline}.pf-l-flex>.pf-m-align-self-stretch-on-lg{align-self:stretch}}@media (min-width:1200px){.pf-l-flex.pf-m-flex-on-xl{display:var(--pf-l-flex--Display)}.pf-l-flex.pf-m-inline-flex-on-xl{--pf-l-flex--Display:inline-flex}.pf-l-flex.pf-m-column-on-xl{flex-direction:column;align-items:normal}.pf-l-flex.pf-m-column-on-xl>*{margin:0 0 var(--pf-l-flex--spacer) 0}.pf-l-flex.pf-m-column-reverse-on-xl{flex-direction:column-reverse;align-items:normal}.pf-l-flex.pf-m-column-reverse-on-xl>*{margin:var(--pf-l-flex--spacer) 0 0 0}.pf-l-flex.pf-m-row-on-xl{flex-direction:row;align-items:var(--pf-l-flex--m-row--AlignItems)}.pf-l-flex.pf-m-row-on-xl>*{margin:0 var(--pf-l-flex--spacer) 0 0}.pf-l-flex.pf-m-row-reverse-on-xl{flex-direction:row-reverse;align-items:var(--pf-l-flex--m-row-reverse--AlignItems)}.pf-l-flex.pf-m-row-reverse-on-xl>*{margin:0 0 0 var(--pf-l-flex--spacer)}.pf-l-flex.pf-m-wrap-on-xl{flex-wrap:wrap}.pf-l-flex.pf-m-wrap-reverse-on-xl{flex-wrap:wrap-reverse}.pf-l-flex.pf-m-nowrap-on-xl{flex-wrap:nowrap}.pf-l-flex.pf-m-justify-content-flex-start-on-xl{justify-content:flex-start}.pf-l-flex.pf-m-justify-content-flex-end-on-xl{justify-content:flex-end}.pf-l-flex.pf-m-justify-content-center-on-xl{justify-content:center}.pf-l-flex.pf-m-justify-content-space-between-on-xl{justify-content:space-between}.pf-l-flex.pf-m-justify-content-space-around-on-xl{justify-content:space-around}.pf-l-flex.pf-m-justify-content-space-evenly-on-xl{justify-content:space-evenly}.pf-l-flex.pf-m-align-items-flex-start-on-xl{align-items:flex-start}.pf-l-flex.pf-m-align-items-flex-end-on-xl{align-items:flex-end}.pf-l-flex.pf-m-align-items-center-on-xl{align-items:center}.pf-l-flex.pf-m-align-items-stretch-on-xl{align-items:stretch}.pf-l-flex.pf-m-align-items-baseline-on-xl{align-items:baseline}.pf-l-flex.pf-m-align-content-flex-start-on-xl{align-content:flex-start}.pf-l-flex.pf-m-align-content-flex-end-on-xl{align-content:flex-end}.pf-l-flex.pf-m-align-content-center-on-xl{align-content:center}.pf-l-flex.pf-m-align-content-stretch-on-xl{align-content:stretch}.pf-l-flex.pf-m-align-content-space-between-on-xl{align-content:space-between}.pf-l-flex.pf-m-align-content-space-around-on-xl{align-content:space-around}.pf-l-flex>.pf-m-align-right-on-xl{margin-left:auto}.pf-l-flex>.pf-m-align-left-on-xl{margin-left:0}.pf-l-flex>.pf-m-grow-on-xl{flex-grow:1}.pf-l-flex>.pf-m-shrink-on-xl{flex-shrink:1}.pf-l-flex>.pf-m-full-width-on-xl{width:100%;margin-right:0}.pf-l-flex>.pf-m-flex-1-on-xl{flex:1 0 0}.pf-l-flex>.pf-m-flex-2-on-xl{flex:2 0 0}.pf-l-flex>.pf-m-flex-3-on-xl{flex:3 0 0}.pf-l-flex>.pf-m-flex-4-on-xl{flex:4 0 0}.pf-l-flex>.pf-m-flex-default-on-xl{flex:0 1 auto}.pf-l-flex>.pf-m-flex-none-on-xl{flex:none}.pf-l-flex>.pf-m-align-self-flex-start-on-xl{align-self:flex-start}.pf-l-flex>.pf-m-align-self-flex-end-on-xl{align-self:flex-end}.pf-l-flex>.pf-m-align-self-center-on-xl{align-self:center}.pf-l-flex>.pf-m-align-self-baseline-on-xl{align-self:baseline}.pf-l-flex>.pf-m-align-self-stretch-on-xl{align-self:stretch}}@media (min-width:1450px){.pf-l-flex.pf-m-flex-on-2xl{display:var(--pf-l-flex--Display)}.pf-l-flex.pf-m-inline-flex-on-2xl{--pf-l-flex--Display:inline-flex}.pf-l-flex.pf-m-column-on-2xl{flex-direction:column;align-items:normal}.pf-l-flex.pf-m-column-on-2xl>*{margin:0 0 var(--pf-l-flex--spacer) 0}.pf-l-flex.pf-m-column-reverse-on-2xl{flex-direction:column-reverse;align-items:normal}.pf-l-flex.pf-m-column-reverse-on-2xl>*{margin:var(--pf-l-flex--spacer) 0 0 0}.pf-l-flex.pf-m-row-on-2xl{flex-direction:row;align-items:var(--pf-l-flex--m-row--AlignItems)}.pf-l-flex.pf-m-row-on-2xl>*{margin:0 var(--pf-l-flex--spacer) 0 0}.pf-l-flex.pf-m-row-reverse-on-2xl{flex-direction:row-reverse;align-items:var(--pf-l-flex--m-row-reverse--AlignItems)}.pf-l-flex.pf-m-row-reverse-on-2xl>*{margin:0 0 0 var(--pf-l-flex--spacer)}.pf-l-flex.pf-m-wrap-on-2xl{flex-wrap:wrap}.pf-l-flex.pf-m-wrap-reverse-on-2xl{flex-wrap:wrap-reverse}.pf-l-flex.pf-m-nowrap-on-2xl{flex-wrap:nowrap}.pf-l-flex.pf-m-justify-content-flex-start-on-2xl{justify-content:flex-start}.pf-l-flex.pf-m-justify-content-flex-end-on-2xl{justify-content:flex-end}.pf-l-flex.pf-m-justify-content-center-on-2xl{justify-content:center}.pf-l-flex.pf-m-justify-content-space-between-on-2xl{justify-content:space-between}.pf-l-flex.pf-m-justify-content-space-around-on-2xl{justify-content:space-around}.pf-l-flex.pf-m-justify-content-space-evenly-on-2xl{justify-content:space-evenly}.pf-l-flex.pf-m-align-items-flex-start-on-2xl{align-items:flex-start}.pf-l-flex.pf-m-align-items-flex-end-on-2xl{align-items:flex-end}.pf-l-flex.pf-m-align-items-center-on-2xl{align-items:center}.pf-l-flex.pf-m-align-items-stretch-on-2xl{align-items:stretch}.pf-l-flex.pf-m-align-items-baseline-on-2xl{align-items:baseline}.pf-l-flex.pf-m-align-content-flex-start-on-2xl{align-content:flex-start}.pf-l-flex.pf-m-align-content-flex-end-on-2xl{align-content:flex-end}.pf-l-flex.pf-m-align-content-center-on-2xl{align-content:center}.pf-l-flex.pf-m-align-content-stretch-on-2xl{align-content:stretch}.pf-l-flex.pf-m-align-content-space-between-on-2xl{align-content:space-between}.pf-l-flex.pf-m-align-content-space-around-on-2xl{align-content:space-around}.pf-l-flex>.pf-m-align-right-on-2xl{margin-left:auto}.pf-l-flex>.pf-m-align-left-on-2xl{margin-left:0}.pf-l-flex>.pf-m-grow-on-2xl{flex-grow:1}.pf-l-flex>.pf-m-shrink-on-2xl{flex-shrink:1}.pf-l-flex>.pf-m-full-width-on-2xl{width:100%;margin-right:0}.pf-l-flex>.pf-m-flex-1-on-2xl{flex:1 0 0}.pf-l-flex>.pf-m-flex-2-on-2xl{flex:2 0 0}.pf-l-flex>.pf-m-flex-3-on-2xl{flex:3 0 0}.pf-l-flex>.pf-m-flex-4-on-2xl{flex:4 0 0}.pf-l-flex>.pf-m-flex-default-on-2xl{flex:0 1 auto}.pf-l-flex>.pf-m-flex-none-on-2xl{flex:none}.pf-l-flex>.pf-m-align-self-flex-start-on-2xl{align-self:flex-start}.pf-l-flex>.pf-m-align-self-flex-end-on-2xl{align-self:flex-end}.pf-l-flex>.pf-m-align-self-center-on-2xl{align-self:center}.pf-l-flex>.pf-m-align-self-baseline-on-2xl{align-self:baseline}.pf-l-flex>.pf-m-align-self-stretch-on-2xl{align-self:stretch}}.pf-l-flex.pf-m-space-items-none>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex.pf-m-space-items-none>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xs>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex.pf-m-space-items-xs>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex.pf-m-space-items-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex.pf-m-space-items-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex.pf-m-space-items-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex.pf-m-space-items-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex.pf-m-space-items-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-3xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex.pf-m-space-items-3xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-4xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}.pf-l-flex.pf-m-space-items-4xl>:last-child{--pf-l-flex--spacer:0}@media (min-width:576px){.pf-l-flex.pf-m-space-items-none-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex.pf-m-space-items-none-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xs-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex.pf-m-space-items-xs-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-sm-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex.pf-m-space-items-sm-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-md-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex.pf-m-space-items-md-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-lg-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex.pf-m-space-items-lg-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xl-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex.pf-m-space-items-xl-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-2xl-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex.pf-m-space-items-2xl-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-3xl-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex.pf-m-space-items-3xl-on-sm>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-4xl-on-sm>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}.pf-l-flex.pf-m-space-items-4xl-on-sm>:last-child{--pf-l-flex--spacer:0}}@media (min-width:768px){.pf-l-flex.pf-m-space-items-none-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex.pf-m-space-items-none-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xs-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex.pf-m-space-items-xs-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-sm-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex.pf-m-space-items-sm-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-md-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex.pf-m-space-items-md-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-lg-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex.pf-m-space-items-lg-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xl-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex.pf-m-space-items-xl-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-2xl-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex.pf-m-space-items-2xl-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-3xl-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex.pf-m-space-items-3xl-on-md>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-4xl-on-md>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}.pf-l-flex.pf-m-space-items-4xl-on-md>:last-child{--pf-l-flex--spacer:0}}@media (min-width:992px){.pf-l-flex.pf-m-space-items-none-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex.pf-m-space-items-none-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xs-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex.pf-m-space-items-xs-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-sm-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex.pf-m-space-items-sm-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-md-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex.pf-m-space-items-md-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-lg-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex.pf-m-space-items-lg-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xl-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex.pf-m-space-items-xl-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-2xl-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex.pf-m-space-items-2xl-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-3xl-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex.pf-m-space-items-3xl-on-lg>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-4xl-on-lg>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}.pf-l-flex.pf-m-space-items-4xl-on-lg>:last-child{--pf-l-flex--spacer:0}}@media (min-width:1200px){.pf-l-flex.pf-m-space-items-none-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex.pf-m-space-items-none-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xs-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex.pf-m-space-items-xs-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-sm-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex.pf-m-space-items-sm-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-md-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex.pf-m-space-items-md-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-lg-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex.pf-m-space-items-lg-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xl-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex.pf-m-space-items-xl-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-2xl-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex.pf-m-space-items-2xl-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-3xl-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex.pf-m-space-items-3xl-on-xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-4xl-on-xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}.pf-l-flex.pf-m-space-items-4xl-on-xl>:last-child{--pf-l-flex--spacer:0}}@media (min-width:1450px){.pf-l-flex.pf-m-space-items-none-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex.pf-m-space-items-none-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xs-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex.pf-m-space-items-xs-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-sm-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex.pf-m-space-items-sm-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-md-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex.pf-m-space-items-md-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-lg-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex.pf-m-space-items-lg-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-xl-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex.pf-m-space-items-xl-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-2xl-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex.pf-m-space-items-2xl-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-3xl-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex.pf-m-space-items-3xl-on-2xl>:last-child{--pf-l-flex--spacer:0}.pf-l-flex.pf-m-space-items-4xl-on-2xl>*{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}.pf-l-flex.pf-m-space-items-4xl-on-2xl>:last-child{--pf-l-flex--spacer:0}}.pf-l-flex .pf-m-spacer-none,.pf-l-flex .pf-m-spacer-none:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex .pf-m-spacer-xs,.pf-l-flex .pf-m-spacer-xs:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex .pf-m-spacer-sm,.pf-l-flex .pf-m-spacer-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex .pf-m-spacer-md,.pf-l-flex .pf-m-spacer-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex .pf-m-spacer-lg,.pf-l-flex .pf-m-spacer-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex .pf-m-spacer-xl,.pf-l-flex .pf-m-spacer-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex .pf-m-spacer-2xl,.pf-l-flex .pf-m-spacer-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex .pf-m-spacer-3xl,.pf-l-flex .pf-m-spacer-3xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex .pf-m-spacer-4xl,.pf-l-flex .pf-m-spacer-4xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}@media (min-width:576px){.pf-l-flex .pf-m-spacer-none-on-sm,.pf-l-flex .pf-m-spacer-none-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex .pf-m-spacer-xs-on-sm,.pf-l-flex .pf-m-spacer-xs-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex .pf-m-spacer-sm-on-sm,.pf-l-flex .pf-m-spacer-sm-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex .pf-m-spacer-md-on-sm,.pf-l-flex .pf-m-spacer-md-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex .pf-m-spacer-lg-on-sm,.pf-l-flex .pf-m-spacer-lg-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex .pf-m-spacer-xl-on-sm,.pf-l-flex .pf-m-spacer-xl-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex .pf-m-spacer-2xl-on-sm,.pf-l-flex .pf-m-spacer-2xl-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex .pf-m-spacer-3xl-on-sm,.pf-l-flex .pf-m-spacer-3xl-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex .pf-m-spacer-4xl-on-sm,.pf-l-flex .pf-m-spacer-4xl-on-sm:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}}@media (min-width:768px){.pf-l-flex .pf-m-spacer-none-on-md,.pf-l-flex .pf-m-spacer-none-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex .pf-m-spacer-xs-on-md,.pf-l-flex .pf-m-spacer-xs-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex .pf-m-spacer-sm-on-md,.pf-l-flex .pf-m-spacer-sm-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex .pf-m-spacer-md-on-md,.pf-l-flex .pf-m-spacer-md-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex .pf-m-spacer-lg-on-md,.pf-l-flex .pf-m-spacer-lg-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex .pf-m-spacer-xl-on-md,.pf-l-flex .pf-m-spacer-xl-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex .pf-m-spacer-2xl-on-md,.pf-l-flex .pf-m-spacer-2xl-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex .pf-m-spacer-3xl-on-md,.pf-l-flex .pf-m-spacer-3xl-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex .pf-m-spacer-4xl-on-md,.pf-l-flex .pf-m-spacer-4xl-on-md:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}}@media (min-width:992px){.pf-l-flex .pf-m-spacer-none-on-lg,.pf-l-flex .pf-m-spacer-none-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex .pf-m-spacer-xs-on-lg,.pf-l-flex .pf-m-spacer-xs-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex .pf-m-spacer-sm-on-lg,.pf-l-flex .pf-m-spacer-sm-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex .pf-m-spacer-md-on-lg,.pf-l-flex .pf-m-spacer-md-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex .pf-m-spacer-lg-on-lg,.pf-l-flex .pf-m-spacer-lg-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex .pf-m-spacer-xl-on-lg,.pf-l-flex .pf-m-spacer-xl-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex .pf-m-spacer-2xl-on-lg,.pf-l-flex .pf-m-spacer-2xl-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex .pf-m-spacer-3xl-on-lg,.pf-l-flex .pf-m-spacer-3xl-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex .pf-m-spacer-4xl-on-lg,.pf-l-flex .pf-m-spacer-4xl-on-lg:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}}@media (min-width:1200px){.pf-l-flex .pf-m-spacer-none-on-xl,.pf-l-flex .pf-m-spacer-none-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex .pf-m-spacer-xs-on-xl,.pf-l-flex .pf-m-spacer-xs-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex .pf-m-spacer-sm-on-xl,.pf-l-flex .pf-m-spacer-sm-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex .pf-m-spacer-md-on-xl,.pf-l-flex .pf-m-spacer-md-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex .pf-m-spacer-lg-on-xl,.pf-l-flex .pf-m-spacer-lg-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex .pf-m-spacer-xl-on-xl,.pf-l-flex .pf-m-spacer-xl-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex .pf-m-spacer-2xl-on-xl,.pf-l-flex .pf-m-spacer-2xl-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex .pf-m-spacer-3xl-on-xl,.pf-l-flex .pf-m-spacer-3xl-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex .pf-m-spacer-4xl-on-xl,.pf-l-flex .pf-m-spacer-4xl-on-xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}}@media (min-width:1450px){.pf-l-flex .pf-m-spacer-none-on-2xl,.pf-l-flex .pf-m-spacer-none-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--none)}.pf-l-flex .pf-m-spacer-xs-on-2xl,.pf-l-flex .pf-m-spacer-xs-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xs)}.pf-l-flex .pf-m-spacer-sm-on-2xl,.pf-l-flex .pf-m-spacer-sm-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--sm)}.pf-l-flex .pf-m-spacer-md-on-2xl,.pf-l-flex .pf-m-spacer-md-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--md)}.pf-l-flex .pf-m-spacer-lg-on-2xl,.pf-l-flex .pf-m-spacer-lg-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--lg)}.pf-l-flex .pf-m-spacer-xl-on-2xl,.pf-l-flex .pf-m-spacer-xl-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--xl)}.pf-l-flex .pf-m-spacer-2xl-on-2xl,.pf-l-flex .pf-m-spacer-2xl-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--2xl)}.pf-l-flex .pf-m-spacer-3xl-on-2xl,.pf-l-flex .pf-m-spacer-3xl-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--3xl)}.pf-l-flex .pf-m-spacer-4xl-on-2xl,.pf-l-flex .pf-m-spacer-4xl-on-2xl:last-child{--pf-l-flex--spacer:var(--pf-l-flex--spacer--4xl)}}.pf-l-gallery{--pf-l-gallery--m-gutter--GridGap:var(--pf-global--gutter);--pf-l-gallery--GridTemplateColumns--min:250px;--pf-l-gallery--GridTemplateColumns--max:1fr;--pf-l-gallery--GridTemplateColumns:repeat(auto-fill,minmax(var(--pf-l-gallery--GridTemplateColumns--minmax--min),var(--pf-l-gallery--GridTemplateColumns--minmax--max)));--pf-l-gallery--GridTemplateRows:auto;display:grid;grid-template-columns:var(--pf-l-gallery--GridTemplateColumns);grid-template-rows:var(--pf-l-gallery--GridTemplateRows);--pf-l-gallery--GridTemplateColumns--minmax--min:var(--pf-l-gallery--GridTemplateColumns--min);--pf-l-gallery--GridTemplateColumns--minmax--max:var(--pf-l-gallery--GridTemplateColumns--max)}.pf-l-gallery.pf-m-gutter{grid-gap:var(--pf-l-gallery--m-gutter--GridGap)}@media (min-width:576px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--min:var(--pf-l-gallery--GridTemplateColumns--min-on-sm,var(--pf-l-gallery--GridTemplateColumns--min))}}@media (min-width:768px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--min:var(--pf-l-gallery--GridTemplateColumns--min-on-md,var(--pf-l-gallery--GridTemplateColumns--min-on-sm,var(--pf-l-gallery--GridTemplateColumns--min)))}}@media (min-width:992px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--min:var(--pf-l-gallery--GridTemplateColumns--min-on-lg,var(--pf-l-gallery--GridTemplateColumns--min-on-md,var(--pf-l-gallery--GridTemplateColumns--min-on-sm,var(--pf-l-gallery--GridTemplateColumns--min))))}}@media (min-width:1200px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--min:var(--pf-l-gallery--GridTemplateColumns--min-on-xl,var(--pf-l-gallery--GridTemplateColumns--min-on-lg,var(--pf-l-gallery--GridTemplateColumns--min-on-md,var(--pf-l-gallery--GridTemplateColumns--min-on-sm,var(--pf-l-gallery--GridTemplateColumns--min)))))}}@media (min-width:1450px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--min:var(--pf-l-gallery--GridTemplateColumns--min-on-2xl,var(--pf-l-gallery--GridTemplateColumns--min-on-xl,var(--pf-l-gallery--GridTemplateColumns--min-on-lg,var(--pf-l-gallery--GridTemplateColumns--min-on-md,var(--pf-l-gallery--GridTemplateColumns--min-on-sm,var(--pf-l-gallery--GridTemplateColumns--min))))))}}@media (min-width:576px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--max:var(--pf-l-gallery--GridTemplateColumns--max-on-sm,var(--pf-l-gallery--GridTemplateColumns--max))}}@media (min-width:768px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--max:var(--pf-l-gallery--GridTemplateColumns--max-on-md,var(--pf-l-gallery--GridTemplateColumns--max-on-sm,var(--pf-l-gallery--GridTemplateColumns--max)))}}@media (min-width:992px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--max:var(--pf-l-gallery--GridTemplateColumns--max-on-lg,var(--pf-l-gallery--GridTemplateColumns--max-on-md,var(--pf-l-gallery--GridTemplateColumns--max-on-sm,var(--pf-l-gallery--GridTemplateColumns--max))))}}@media (min-width:1200px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--max:var(--pf-l-gallery--GridTemplateColumns--max-on-xl,var(--pf-l-gallery--GridTemplateColumns--max-on-lg,var(--pf-l-gallery--GridTemplateColumns--max-on-md,var(--pf-l-gallery--GridTemplateColumns--max-on-sm,var(--pf-l-gallery--GridTemplateColumns--max)))))}}@media (min-width:1450px){.pf-l-gallery{--pf-l-gallery--GridTemplateColumns--minmax--max:var(--pf-l-gallery--GridTemplateColumns--max-on-2xl,var(--pf-l-gallery--GridTemplateColumns--max-on-xl,var(--pf-l-gallery--GridTemplateColumns--max-on-lg,var(--pf-l-gallery--GridTemplateColumns--max-on-md,var(--pf-l-gallery--GridTemplateColumns--max-on-sm,var(--pf-l-gallery--GridTemplateColumns--max))))))}}.pf-l-grid{--pf-l-grid--m-gutter--GridGap:var(--pf-global--gutter);--pf-l-grid__item--GridColumnStart:auto;--pf-l-grid__item--GridColumnEnd:span 12;--pf-l-grid--item--Order:0;display:grid;grid-template-columns:repeat(12,[col-start] 1fr)}.pf-l-grid .pf-l-grid__item,.pf-l-grid>*{min-width:0;min-height:0;grid-column-start:var(--pf-l-grid__item--GridColumnStart);grid-column-end:var(--pf-l-grid__item--GridColumnEnd);order:var(--pf-l-grid--item--Order)}@media (min-width:576px){.pf-l-grid .pf-l-grid__item,.pf-l-grid>*{order:var(--pf-l-grid--item--Order-on-sm,var(--pf-l-grid--item--Order))}}@media (min-width:768px){.pf-l-grid .pf-l-grid__item,.pf-l-grid>*{order:var(--pf-l-grid--item--Order-on-md,var(--pf-l-grid--item--Order-on-sm,var(--pf-l-grid--item--Order)))}}@media (min-width:992px){.pf-l-grid .pf-l-grid__item,.pf-l-grid>*{order:var(--pf-l-grid--item--Order-on-lg,var(--pf-l-grid--item--Order-on-md,var(--pf-l-grid--item--Order-on-sm,var(--pf-l-grid--item--Order))))}}@media (min-width:1200px){.pf-l-grid .pf-l-grid__item,.pf-l-grid>*{order:var(--pf-l-grid--item--Order-on-xl,var(--pf-l-grid--item--Order-on-lg,var(--pf-l-grid--item--Order-on-md,var(--pf-l-grid--item--Order-on-sm,var(--pf-l-grid--item--Order)))))}}@media (min-width:1450px){.pf-l-grid .pf-l-grid__item,.pf-l-grid>*{order:var(--pf-l-grid--item--Order-on-2xl,var(--pf-l-grid--item--Order-on-xl,var(--pf-l-grid--item--Order-on-lg,var(--pf-l-grid--item--Order-on-md,var(--pf-l-grid--item--Order-on-sm,var(--pf-l-grid--item--Order))))))}}.pf-l-grid.pf-m-all-1-col>*{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid.pf-m-all-2-col>*{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid.pf-m-all-3-col>*{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid.pf-m-all-4-col>*{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid.pf-m-all-5-col>*{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid.pf-m-all-6-col>*{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid.pf-m-all-7-col>*{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid.pf-m-all-8-col>*{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid.pf-m-all-9-col>*{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid.pf-m-all-10-col>*{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid.pf-m-all-11-col>*{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid.pf-m-all-12-col>*{--pf-l-grid__item--GridColumnEnd:span 12}@media screen and (min-width:576px){.pf-l-grid.pf-m-all-1-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid.pf-m-all-2-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid.pf-m-all-3-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid.pf-m-all-4-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid.pf-m-all-5-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid.pf-m-all-6-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid.pf-m-all-7-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid.pf-m-all-8-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid.pf-m-all-9-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid.pf-m-all-10-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid.pf-m-all-11-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid.pf-m-all-12-col-on-sm>*{--pf-l-grid__item--GridColumnEnd:span 12}}@media screen and (min-width:768px){.pf-l-grid.pf-m-all-1-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid.pf-m-all-2-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid.pf-m-all-3-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid.pf-m-all-4-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid.pf-m-all-5-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid.pf-m-all-6-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid.pf-m-all-7-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid.pf-m-all-8-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid.pf-m-all-9-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid.pf-m-all-10-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid.pf-m-all-11-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid.pf-m-all-12-col-on-md>*{--pf-l-grid__item--GridColumnEnd:span 12}}@media screen and (min-width:992px){.pf-l-grid.pf-m-all-1-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid.pf-m-all-2-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid.pf-m-all-3-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid.pf-m-all-4-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid.pf-m-all-5-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid.pf-m-all-6-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid.pf-m-all-7-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid.pf-m-all-8-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid.pf-m-all-9-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid.pf-m-all-10-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid.pf-m-all-11-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid.pf-m-all-12-col-on-lg>*{--pf-l-grid__item--GridColumnEnd:span 12}}@media screen and (min-width:1200px){.pf-l-grid.pf-m-all-1-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid.pf-m-all-2-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid.pf-m-all-3-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid.pf-m-all-4-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid.pf-m-all-5-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid.pf-m-all-6-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid.pf-m-all-7-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid.pf-m-all-8-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid.pf-m-all-9-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid.pf-m-all-10-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid.pf-m-all-11-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid.pf-m-all-12-col-on-xl>*{--pf-l-grid__item--GridColumnEnd:span 12}}@media screen and (min-width:1450px){.pf-l-grid.pf-m-all-1-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid.pf-m-all-2-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid.pf-m-all-3-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid.pf-m-all-4-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid.pf-m-all-5-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid.pf-m-all-6-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid.pf-m-all-7-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid.pf-m-all-8-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid.pf-m-all-9-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid.pf-m-all-10-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid.pf-m-all-11-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid.pf-m-all-12-col-on-2xl>*{--pf-l-grid__item--GridColumnEnd:span 12}}.pf-l-grid>.pf-m-1-col{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid>.pf-m-2-col{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid>.pf-m-3-col{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid>.pf-m-4-col{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid>.pf-m-5-col{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid>.pf-m-6-col{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid>.pf-m-7-col{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid>.pf-m-8-col{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid>.pf-m-9-col{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid>.pf-m-10-col{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid>.pf-m-11-col{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid>.pf-m-12-col{--pf-l-grid__item--GridColumnEnd:span 12}.pf-l-grid>.pf-m-offset-1-col{--pf-l-grid__item--GridColumnStart:col-start 2}.pf-l-grid>.pf-m-offset-2-col{--pf-l-grid__item--GridColumnStart:col-start 3}.pf-l-grid>.pf-m-offset-3-col{--pf-l-grid__item--GridColumnStart:col-start 4}.pf-l-grid>.pf-m-offset-4-col{--pf-l-grid__item--GridColumnStart:col-start 5}.pf-l-grid>.pf-m-offset-5-col{--pf-l-grid__item--GridColumnStart:col-start 6}.pf-l-grid>.pf-m-offset-6-col{--pf-l-grid__item--GridColumnStart:col-start 7}.pf-l-grid>.pf-m-offset-7-col{--pf-l-grid__item--GridColumnStart:col-start 8}.pf-l-grid>.pf-m-offset-8-col{--pf-l-grid__item--GridColumnStart:col-start 9}.pf-l-grid>.pf-m-offset-9-col{--pf-l-grid__item--GridColumnStart:col-start 10}.pf-l-grid>.pf-m-offset-10-col{--pf-l-grid__item--GridColumnStart:col-start 11}.pf-l-grid>.pf-m-offset-11-col{--pf-l-grid__item--GridColumnStart:col-start 12}.pf-l-grid>.pf-m-offset-12-col{--pf-l-grid__item--GridColumnStart:col-start 13}.pf-l-grid>.pf-m-1-row{grid-row:span 1}.pf-l-grid>.pf-m-2-row{grid-row:span 2}.pf-l-grid>.pf-m-3-row{grid-row:span 3}.pf-l-grid>.pf-m-4-row{grid-row:span 4}.pf-l-grid>.pf-m-5-row{grid-row:span 5}.pf-l-grid>.pf-m-6-row{grid-row:span 6}.pf-l-grid>.pf-m-7-row{grid-row:span 7}.pf-l-grid>.pf-m-8-row{grid-row:span 8}.pf-l-grid>.pf-m-9-row{grid-row:span 9}.pf-l-grid>.pf-m-10-row{grid-row:span 10}.pf-l-grid>.pf-m-11-row{grid-row:span 11}.pf-l-grid>.pf-m-12-row{grid-row:span 12}@media screen and (min-width:576px){.pf-l-grid>.pf-m-1-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid>.pf-m-2-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid>.pf-m-3-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid>.pf-m-4-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid>.pf-m-5-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid>.pf-m-6-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid>.pf-m-7-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid>.pf-m-8-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid>.pf-m-9-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid>.pf-m-10-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid>.pf-m-11-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid>.pf-m-12-col-on-sm{--pf-l-grid__item--GridColumnEnd:span 12}.pf-l-grid>.pf-m-offset-1-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 2}.pf-l-grid>.pf-m-offset-2-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 3}.pf-l-grid>.pf-m-offset-3-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 4}.pf-l-grid>.pf-m-offset-4-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 5}.pf-l-grid>.pf-m-offset-5-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 6}.pf-l-grid>.pf-m-offset-6-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 7}.pf-l-grid>.pf-m-offset-7-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 8}.pf-l-grid>.pf-m-offset-8-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 9}.pf-l-grid>.pf-m-offset-9-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 10}.pf-l-grid>.pf-m-offset-10-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 11}.pf-l-grid>.pf-m-offset-11-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 12}.pf-l-grid>.pf-m-offset-12-col-on-sm{--pf-l-grid__item--GridColumnStart:col-start 13}.pf-l-grid>.pf-m-1-row-on-sm{grid-row:span 1}.pf-l-grid>.pf-m-2-row-on-sm{grid-row:span 2}.pf-l-grid>.pf-m-3-row-on-sm{grid-row:span 3}.pf-l-grid>.pf-m-4-row-on-sm{grid-row:span 4}.pf-l-grid>.pf-m-5-row-on-sm{grid-row:span 5}.pf-l-grid>.pf-m-6-row-on-sm{grid-row:span 6}.pf-l-grid>.pf-m-7-row-on-sm{grid-row:span 7}.pf-l-grid>.pf-m-8-row-on-sm{grid-row:span 8}.pf-l-grid>.pf-m-9-row-on-sm{grid-row:span 9}.pf-l-grid>.pf-m-10-row-on-sm{grid-row:span 10}.pf-l-grid>.pf-m-11-row-on-sm{grid-row:span 11}.pf-l-grid>.pf-m-12-row-on-sm{grid-row:span 12}}@media screen and (min-width:768px){.pf-l-grid>.pf-m-1-col-on-md{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid>.pf-m-2-col-on-md{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid>.pf-m-3-col-on-md{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid>.pf-m-4-col-on-md{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid>.pf-m-5-col-on-md{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid>.pf-m-6-col-on-md{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid>.pf-m-7-col-on-md{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid>.pf-m-8-col-on-md{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid>.pf-m-9-col-on-md{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid>.pf-m-10-col-on-md{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid>.pf-m-11-col-on-md{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid>.pf-m-12-col-on-md{--pf-l-grid__item--GridColumnEnd:span 12}.pf-l-grid>.pf-m-offset-1-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 2}.pf-l-grid>.pf-m-offset-2-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 3}.pf-l-grid>.pf-m-offset-3-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 4}.pf-l-grid>.pf-m-offset-4-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 5}.pf-l-grid>.pf-m-offset-5-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 6}.pf-l-grid>.pf-m-offset-6-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 7}.pf-l-grid>.pf-m-offset-7-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 8}.pf-l-grid>.pf-m-offset-8-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 9}.pf-l-grid>.pf-m-offset-9-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 10}.pf-l-grid>.pf-m-offset-10-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 11}.pf-l-grid>.pf-m-offset-11-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 12}.pf-l-grid>.pf-m-offset-12-col-on-md{--pf-l-grid__item--GridColumnStart:col-start 13}.pf-l-grid>.pf-m-1-row-on-md{grid-row:span 1}.pf-l-grid>.pf-m-2-row-on-md{grid-row:span 2}.pf-l-grid>.pf-m-3-row-on-md{grid-row:span 3}.pf-l-grid>.pf-m-4-row-on-md{grid-row:span 4}.pf-l-grid>.pf-m-5-row-on-md{grid-row:span 5}.pf-l-grid>.pf-m-6-row-on-md{grid-row:span 6}.pf-l-grid>.pf-m-7-row-on-md{grid-row:span 7}.pf-l-grid>.pf-m-8-row-on-md{grid-row:span 8}.pf-l-grid>.pf-m-9-row-on-md{grid-row:span 9}.pf-l-grid>.pf-m-10-row-on-md{grid-row:span 10}.pf-l-grid>.pf-m-11-row-on-md{grid-row:span 11}.pf-l-grid>.pf-m-12-row-on-md{grid-row:span 12}}@media screen and (min-width:992px){.pf-l-grid>.pf-m-1-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid>.pf-m-2-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid>.pf-m-3-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid>.pf-m-4-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid>.pf-m-5-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid>.pf-m-6-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid>.pf-m-7-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid>.pf-m-8-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid>.pf-m-9-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid>.pf-m-10-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid>.pf-m-11-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid>.pf-m-12-col-on-lg{--pf-l-grid__item--GridColumnEnd:span 12}.pf-l-grid>.pf-m-offset-1-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 2}.pf-l-grid>.pf-m-offset-2-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 3}.pf-l-grid>.pf-m-offset-3-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 4}.pf-l-grid>.pf-m-offset-4-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 5}.pf-l-grid>.pf-m-offset-5-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 6}.pf-l-grid>.pf-m-offset-6-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 7}.pf-l-grid>.pf-m-offset-7-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 8}.pf-l-grid>.pf-m-offset-8-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 9}.pf-l-grid>.pf-m-offset-9-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 10}.pf-l-grid>.pf-m-offset-10-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 11}.pf-l-grid>.pf-m-offset-11-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 12}.pf-l-grid>.pf-m-offset-12-col-on-lg{--pf-l-grid__item--GridColumnStart:col-start 13}.pf-l-grid>.pf-m-1-row-on-lg{grid-row:span 1}.pf-l-grid>.pf-m-2-row-on-lg{grid-row:span 2}.pf-l-grid>.pf-m-3-row-on-lg{grid-row:span 3}.pf-l-grid>.pf-m-4-row-on-lg{grid-row:span 4}.pf-l-grid>.pf-m-5-row-on-lg{grid-row:span 5}.pf-l-grid>.pf-m-6-row-on-lg{grid-row:span 6}.pf-l-grid>.pf-m-7-row-on-lg{grid-row:span 7}.pf-l-grid>.pf-m-8-row-on-lg{grid-row:span 8}.pf-l-grid>.pf-m-9-row-on-lg{grid-row:span 9}.pf-l-grid>.pf-m-10-row-on-lg{grid-row:span 10}.pf-l-grid>.pf-m-11-row-on-lg{grid-row:span 11}.pf-l-grid>.pf-m-12-row-on-lg{grid-row:span 12}}@media screen and (min-width:1200px){.pf-l-grid>.pf-m-1-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid>.pf-m-2-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid>.pf-m-3-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid>.pf-m-4-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid>.pf-m-5-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid>.pf-m-6-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid>.pf-m-7-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid>.pf-m-8-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid>.pf-m-9-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid>.pf-m-10-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid>.pf-m-11-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid>.pf-m-12-col-on-xl{--pf-l-grid__item--GridColumnEnd:span 12}.pf-l-grid>.pf-m-offset-1-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 2}.pf-l-grid>.pf-m-offset-2-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 3}.pf-l-grid>.pf-m-offset-3-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 4}.pf-l-grid>.pf-m-offset-4-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 5}.pf-l-grid>.pf-m-offset-5-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 6}.pf-l-grid>.pf-m-offset-6-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 7}.pf-l-grid>.pf-m-offset-7-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 8}.pf-l-grid>.pf-m-offset-8-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 9}.pf-l-grid>.pf-m-offset-9-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 10}.pf-l-grid>.pf-m-offset-10-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 11}.pf-l-grid>.pf-m-offset-11-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 12}.pf-l-grid>.pf-m-offset-12-col-on-xl{--pf-l-grid__item--GridColumnStart:col-start 13}.pf-l-grid>.pf-m-1-row-on-xl{grid-row:span 1}.pf-l-grid>.pf-m-2-row-on-xl{grid-row:span 2}.pf-l-grid>.pf-m-3-row-on-xl{grid-row:span 3}.pf-l-grid>.pf-m-4-row-on-xl{grid-row:span 4}.pf-l-grid>.pf-m-5-row-on-xl{grid-row:span 5}.pf-l-grid>.pf-m-6-row-on-xl{grid-row:span 6}.pf-l-grid>.pf-m-7-row-on-xl{grid-row:span 7}.pf-l-grid>.pf-m-8-row-on-xl{grid-row:span 8}.pf-l-grid>.pf-m-9-row-on-xl{grid-row:span 9}.pf-l-grid>.pf-m-10-row-on-xl{grid-row:span 10}.pf-l-grid>.pf-m-11-row-on-xl{grid-row:span 11}.pf-l-grid>.pf-m-12-row-on-xl{grid-row:span 12}}@media screen and (min-width:1450px){.pf-l-grid>.pf-m-1-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 1}.pf-l-grid>.pf-m-2-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 2}.pf-l-grid>.pf-m-3-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 3}.pf-l-grid>.pf-m-4-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 4}.pf-l-grid>.pf-m-5-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 5}.pf-l-grid>.pf-m-6-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 6}.pf-l-grid>.pf-m-7-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 7}.pf-l-grid>.pf-m-8-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 8}.pf-l-grid>.pf-m-9-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 9}.pf-l-grid>.pf-m-10-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 10}.pf-l-grid>.pf-m-11-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 11}.pf-l-grid>.pf-m-12-col-on-2xl{--pf-l-grid__item--GridColumnEnd:span 12}.pf-l-grid>.pf-m-offset-1-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 2}.pf-l-grid>.pf-m-offset-2-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 3}.pf-l-grid>.pf-m-offset-3-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 4}.pf-l-grid>.pf-m-offset-4-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 5}.pf-l-grid>.pf-m-offset-5-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 6}.pf-l-grid>.pf-m-offset-6-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 7}.pf-l-grid>.pf-m-offset-7-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 8}.pf-l-grid>.pf-m-offset-8-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 9}.pf-l-grid>.pf-m-offset-9-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 10}.pf-l-grid>.pf-m-offset-10-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 11}.pf-l-grid>.pf-m-offset-11-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 12}.pf-l-grid>.pf-m-offset-12-col-on-2xl{--pf-l-grid__item--GridColumnStart:col-start 13}.pf-l-grid>.pf-m-1-row-on-2xl{grid-row:span 1}.pf-l-grid>.pf-m-2-row-on-2xl{grid-row:span 2}.pf-l-grid>.pf-m-3-row-on-2xl{grid-row:span 3}.pf-l-grid>.pf-m-4-row-on-2xl{grid-row:span 4}.pf-l-grid>.pf-m-5-row-on-2xl{grid-row:span 5}.pf-l-grid>.pf-m-6-row-on-2xl{grid-row:span 6}.pf-l-grid>.pf-m-7-row-on-2xl{grid-row:span 7}.pf-l-grid>.pf-m-8-row-on-2xl{grid-row:span 8}.pf-l-grid>.pf-m-9-row-on-2xl{grid-row:span 9}.pf-l-grid>.pf-m-10-row-on-2xl{grid-row:span 10}.pf-l-grid>.pf-m-11-row-on-2xl{grid-row:span 11}.pf-l-grid>.pf-m-12-row-on-2xl{grid-row:span 12}}.pf-l-grid.pf-m-gutter{grid-gap:var(--pf-l-grid--m-gutter--GridGap)}.pf-l-level{--pf-l-level--m-gutter--MarginRight:var(--pf-global--gutter);display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.pf-l-level.pf-m-gutter>:not(:last-child){margin-right:var(--pf-l-level--m-gutter--MarginRight)}.pf-l-split{--pf-l-split--m-gutter--MarginRight:var(--pf-global--gutter);display:flex;flex-wrap:nowrap;padding:0;margin:0}.pf-l-split__item.pf-m-fill{flex-grow:1}.pf-l-split.pf-m-gutter>:not(:last-child){margin-right:var(--pf-l-split--m-gutter--MarginRight)}.pf-l-stack{--pf-l-stack--m-gutter--MarginBottom:var(--pf-global--gutter);display:flex;flex-direction:column;height:100%}.pf-l-stack__item.pf-m-fill{flex-grow:1}.pf-l-stack.pf-m-gutter>:not(:last-child){margin-bottom:var(--pf-l-stack--m-gutter--MarginBottom)} -/*# sourceMappingURL=patternfly.min.css.map */ diff --git a/awx/ui/public/static/css/patternfly.min.css.map b/awx/ui/public/static/css/patternfly.min.css.map deleted file mode 100644 index 32fc2565bf5c..000000000000 --- a/awx/ui/public/static/css/patternfly.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["patternfly.min.css"],"names":[],"mappings":"AA6vJA,gBACgB,CA7vJhB,+RACE,yDAA0D,CAC1D,yDAA0D,CAC1D,qEAAsE,CACtE,yEAA0E,CAC1E,4DAA6D,CAC7D,0EAA2E,CAC3E,8EAAiF,CAEnF,0KACE,0DAA2D,CAC3D,0DAA2D,CAC3D,sEAAuE,CACvE,0EAA2E,CAC3E,6DAA8D,CAC9D,oEAAqE,CACrE,6EAAgF,CAChF,kQACE,oFAAuF,CACzF,kRACE,yEAA0E,CAC1E,gFAAiF,CACjF,gFAAiF,CACjF,iFAAkF,CAClF,sFAAuF,CACvF,6FAA8F,CAC9F,6FAA8F,CAC9F,8FAA+F,CAC/F,oEAAqE,CACrE,2EAA4E,CAC5E,2EAA4E,CAC5E,4EAA6E,CAC7E,0EAA2E,CAC3E,iFAAkF,CAClF,iFAAkF,CAClF,kFAAqF,CAEzF,2GACE,gDAAiD,CACjD,yCAA0C,CAC1C,8CAA+C,CAC/C,uEAAwE,CACxE,6EAA8E,CAC9E,yCAA0C,CAC1C,+CAAkD,CAClD,0JACE,sEAAuE,CACvE,4EAA+E,CACjF,oCACE,kLACE,sEAAuE,CACvE,4EAA+E,CACjF,sLACE,uEAAwE,CACxE,6EAAgF,CAAE,CACtF,oCACE,kLACE,sEAAuE,CACvE,4EAA+E,CACjF,sLACE,uEAAwE,CACxE,6EAAgF,CAAE,CACtF,oCACE,kLACE,sEAAuE,CACvE,4EAA+E,CACjF,sLACE,uEAAwE,CACxE,6EAAgF,CAAE,CACtF,qCACE,kLACE,sEAAuE,CACvE,4EAA+E,CACjF,sLACE,uEAAwE,CACxE,6EAAgF,CAAE,CACtF,qCACE,sLACE,sEAAuE,CACvE,4EAA+E,CACjF,0LACE,uEAAwE,CACxE,6EAAgF,CAAE,CAExF,MACE,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,wCAAyC,CACzC,qCAAsC,CACtC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,mCAAoC,CACpC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,qCAAsC,CACtC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,qCAAsC,CACtC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,4CAA6C,CAC7C,4CAA6C,CAC7C,4CAA6C,CAC7C,4CAA6C,CAC7C,4CAA6C,CAC7C,4CAA6C,CAC7C,4CAA6C,CAC7C,6CAA8C,CAC9C,6CAA8C,CAC9C,6CAA8C,CAC9C,6CAA8C,CAC9C,6CAA8C,CAC9C,6CAA8C,CAC9C,6CAA8C,CAC9C,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,uCAAwC,CACxC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,oCAAqC,CACrC,qCAAsC,CACtC,qCAAsC,CACtC,qCAAsC,CACtC,qCAAsC,CACtC,qCAAsC,CACtC,gCAAiC,CACjC,sCAAuC,CACvC,yCAA0C,CAC1C,4CAA6C,CAC7C,+CAAgD,CAChD,+CAAgD,CAChD,8CAA+C,CAC/C,8CAA+C,CAC/C,8CAA+C,CAC/C,8CAA+C,CAC/C,mEAAuE,CACvE,mEAAuE,CACvE,+BAAgC,CAChC,+BAAgC,CAChC,+BAAgC,CAChC,+BAAgC,CAChC,kCAAmC,CACnC,qCAAsC,CACtC,qCAAsC,CACtC,oCAAqC,CACrC,oCAAqC,CACrC,mCAAoC,CACpC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,wCAAyC,CACzC,wCAAyC,CACzC,wCAAyC,CACzC,oCAAqC,CACrC,uCAAwC,CACxC,6CAA8C,CAC9C,yCAA0C,CAC1C,yCAA0C,CAC1C,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,oCAAqC,CACrC,oCAAqC,CACrC,uCAAwC,CACxC,uCAAwC,CACxC,sCAAuC,CACvC,sCAAuC,CACvC,sCAAuC,CACvC,kGAA0G,CAC1G,8EAAkF,CAClF,+EAAmF,CACnF,gFAAoF,CACpF,+EAAmF,CACnF,gGAAwG,CACxG,0EAA8E,CAC9E,2EAA+E,CAC/E,4EAAgF,CAChF,2EAA+E,CAC/E,2FAAmG,CACnG,0EAA8E,CAC9E,2EAA+E,CAC/E,4EAAgF,CAChF,2EAA+E,CAC/E,sFAA8F,CAC9F,mEAAuE,CACvE,oEAAwE,CACxE,qEAAyE,CACzE,oEAAwE,CACxE,mEAAuE,CACvE,uCAAwC,CACxC,4CAA6C,CAC7C,+BAAgC,CAChC,8BAA+B,CAC/B,4BAA6B,CAC7B,8BAA+B,CAC/B,4BAA6B,CAC7B,6BAA8B,CAC9B,6BAA8B,CAC9B,6BAA8B,CAC9B,0CAA2C,CAC3C,wBAAyB,CACzB,8BAA+B,CAC/B,2BAA4B,CAC5B,2BAA4B,CAC5B,2BAA4B,CAC5B,2BAA4B,CAC5B,2BAA4B,CAC5B,4BAA6B,CAC7B,6BAA8B,CAC9B,iCAAkC,CAClC,iCAAkC,CAClC,iCAAkC,CAClC,kCAAmC,CACnC,mCAAoC,CACpC,6BAA8B,CAC9B,uCAAwC,CACxC,uCAAwC,CACxC,8CAA+C,CAC/C,mCAAoC,CACpC,6CAA8C,CAC9C,sCAAuC,CACvC,kDAAmD,CACnD,gCAAiC,CACjC,gCAAiC,CACjC,gCAAiC,CACjC,gCAAiC,CACjC,qCAAsC,CACtC,qCAAsC,CACtC,qCAAsC,CACtC,0CAA2C,CAC3C,2CAA4C,CAC5C,iCAAkC,CAClC,kCAAmC,CACnC,uCAAwC,CACxC,sCAAuC,CACvC,wCAAyC,CACzC,wCAAyC,CACzC,sCAAuC,CACvC,wCAAyC,CACzC,+FAAqG,CACrG,2GAAiH,CACjH,mHAA0H,CAC1H,yNAAsO,CACtO,2JAAoK,CACpK,kCAAmC,CACnC,kCAAmC,CACnC,iCAAkC,CAClC,iCAAkC,CAClC,kCAAmC,CACnC,8BAA+B,CAC/B,kCAAmC,CACnC,iCAAkC,CAClC,kCAAmC,CACnC,mCAAoC,CACpC,sCAAuC,CACvC,gDAAiD,CACjD,iCAAkC,CAClC,2CAA4C,CAC5C,+BAAgC,CAChC,+BAAgC,CAChC,mCAAoC,CACpC,6CAAiE,CACjE,6DAAiE,CACjE,qCAAsC,CACtC,mCAAoC,CACpC,sCAAuC,CACvC,uCAAwC,CACxC,wCAA2C,CAE7C,oBACE,sFAAuF,CACvF,qFAAsF,CACtF,oFAAqF,CACrF,oFAAqF,CACrF,0EAA6E,CAE/E,WACE,yBAA4B,CAC5B,6DAAkE,CAClE,0KAAoL,CACpL,iBAAkB,CAClB,eAAgB,CAChB,iCAAoC,CAEtC,WACE,yBAA4B,CAC5B,4DAAiE,CACjE,wKAAkL,CAClL,iBAAkB,CAClB,eAAgB,CAChB,iCAAoC,CAEtC,WACE,yBAA4B,CAC5B,0DAA+D,CAC/D,oKAA8K,CAC9K,iBAAkB,CAClB,eAAgB,CAChB,iCAAoC,CAEtC,WACE,sBAAyB,CACzB,uDAA4D,CAC5D,8JAAwK,CACxK,iBAAkB,CAClB,eAAgB,CAChB,iCAAoC,CAEtC,WACE,sBAAyB,CACzB,sDAA2D,CAC3D,4JAAsK,CACtK,iBAAkB,CAClB,eAAgB,CAChB,iCAAoC,CAEtC,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,wDAA6D,CAC7D,+SAAqU,CAEvU,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,+DAAoE,CACpE,2UAAiW,CAEnW,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,8DAAmE,CACnE,uUAA6V,CAE/V,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,qEAA0E,CAC1E,mWAAyX,CAE3X,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,yDAA8D,CAC9D,mTAAyU,CAE3U,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,gEAAqE,CACrE,+UAAqW,CAEvW,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,2DAAgE,CAChE,2TAAiV,CAEnV,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,0DAA+D,CAC/D,uTAA6U,CAE/U,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,4DAAiE,CACjE,+TAAqV,CAEvV,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,mEAAwE,CACxE,2VAAiX,CAEnX,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,wDAA6D,CAC7D,+SAAqU,CAEvU,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,+DAAoE,CACpE,2UAAiW,CAEnW,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,6DAAkE,CAClE,mUAAyV,CAE3V,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,oEAAyE,CACzE,+VAAqX,CAEvX,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,yDAA8D,CAC9D,mTAAyU,CAE3U,WACE,oBAAuB,CACvB,iBAAkB,CAClB,eAAgB,CAChB,gEAAqE,CACrE,+UAAqW,CAEvW,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,mEAAwE,CACxE,2VAAiX,CAEnX,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,qEAA0E,CAC1E,mWAAyX,CAE3X,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,sEAA2E,CAC3E,uWAA6X,CAE/X,WACE,yBAA4B,CAC5B,iBAAkB,CAClB,eAAgB,CAChB,kEAAuE,CACvE,uVAA6W,CAE/W,0DACE,SAAU,CACV,QAAS,CACT,4BAA+B,CAEjC,KACE,yBAA6B,CAE/B,kBACE,cAAe,CACf,KAAM,CACN,MAAO,CACP,eAAgB,CAChB,kBAAsB,CACtB,kBAAmB,CACnB,QAAW,CAEb,yGAuBE,SAAU,CACV,QAAW,CAEb,UAEE,WAAc,CAEhB,kBAME,cAAe,CACf,gDAAmD,CAErD,GACE,eAAkB,CAEpB,sCAKE,QAAS,CACT,mBAAoB,CACpB,cAAe,CACf,4CAA6C,CAC7C,kCAAqC,CAEvC,oCAME,cAAe,CACf,WAAc,CAEhB,OACE,QAAW,CAEb,MACE,gBAAiB,CACjB,wBAA2B,CAE7B,MAEE,SAAU,CACV,eAAkB,CAEpB,iBAGE,qBAAwB,CAE1B,KACE,sBAAuB,CACvB,gBAAmB,CAErB,KACE,oDAAqD,CACrD,wCAAyC,CACzC,gDAAiD,CACjD,4CAA6C,CAC7C,eAAgB,CAChB,uDAA0D,CAE5D,EACE,8CAA+C,CAC/C,mCAAoC,CACpC,sDAAyD,CACzD,QACE,6DAA8D,CAC9D,+EAAkF,CAEtF,SAEE,cAAiB,CAEnB,wHAIE,SAAU,CACV,iBAAoB,CAEtB,4GAIE,6BAAgC,CAElC,sBACE,mDAAsD,CAExD,4BACE,4BAA+B,CAEjC,gCACE,gCAAuC,CAEzC,gCACE,+BAAsC,CAExC,2BACE,wBAA2B,CAE7B,2BACE,wBAA2B,CAE7B,6BACE,4BAA+B,CAEjC,4BACE,qBAAwB,CAE1B,4BACE,wBAA2B,CAE7B,4BACE,wBAA2B,CAE7B,kCAKE,iCAAkC,CAClC,kCAAmC,CACnC,oBAAqB,CACrB,iBAAkB,CAClB,mBAAoB,CACpB,mBAAoB,CACpB,aAAgB,CAElB,SACE,mBAAoB,CACpB,iBAAmB,CACnB,uBAA0B,CAE5B,SACE,eAAkB,CAEpB,SACE,gBAAmB,CAErB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,SACE,aAAgB,CAElB,UACE,cAAiB,CAEnB,SACE,iBAAkB,CAClB,YAAe,CAEjB,SACE,oBAAqB,CACrB,iBAAkB,CAClB,cAAiB,CACjB,YACE,iBAAoB,CAExB,SACE,SAAU,CACV,iBAAkB,CAClB,iBAAkB,CAClB,SAAU,CACV,mBAAsB,CAExB,aACE,uBAAyB,CACzB,kBAAmB,CACnB,wBAA2B,CAE7B,gBACE,UAAa,CAEf,iBACE,WAAc,CAEhB,mGAKE,iBAAoB,CAEtB,wGAKE,gBAAmB,CAErB,WACE,oCAAuC,CAEzC,YACE,sCAAyC,CAE3C,mBACE,GACE,sBAAyB,CAC3B,GACE,uBAA2B,CAAE,CAEjC,gBACE,qEAAsE,CACtE,uBAA0B,CAE5B,iBACE,qEAAsE,CACtE,wBAA2B,CAE7B,iBACE,qEAAsE,CACtE,wBAA2B,CAE7B,sBACE,+EAAgF,CAChF,oBAAyB,CAE3B,oBAEE,oBAAyB,CAE3B,2DAHE,+EAK0B,CAF5B,uCAEE,mBAA0B,CAE5B,0HAKE,WAAc,CAEhB,YACE,oBAAqB,CACrB,UAAW,CACX,eAAgB,CAChB,iBAAkB,CAClB,qBAAsB,CACtB,WAAc,CAEhB,8BAEE,MAAO,CACP,iBAAkB,CAClB,iBAAkB,CAClB,UAAa,CAEf,eACE,mBAAsB,CAGxB,eACE,aAAgB,CAElB,cACE,UAAa,CAEf,mBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,uCACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,iDACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mCACE,eAAkB,CAEpB,mCACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yCACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,qCACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,uCACE,eAAkB,CAEpB,4CACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,mCACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,sCACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,oCACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,qCACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,gCACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,+BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,wCACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,2BACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,gBACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,6BACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,iBACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,iCACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,uBACE,eAAkB,CAEpB,8BACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,wBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,yBACE,eAAkB,CAEpB,0BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,oBACE,eAAkB,CAEpB,kCACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,kBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,sBACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,qBACE,eAAkB,CAEpB,4BACE,eAAkB,CAEpB,mBACE,eAAkB,CAEpB,WACE,QAAS,CACT,kBAAsB,CACtB,UAAW,CACX,WAAY,CACZ,eAAgB,CAChB,SAAU,CACV,iBAAkB,CAClB,SAAY,CAEd,uDACE,SAAU,CACV,WAAY,CACZ,QAAS,CACT,gBAAiB,CACjB,eAAgB,CAChB,UAAa,CAEf,WACE,gCAAkC,CAClC,iBAAkB,CAClB,eAAgB,CAChB,+CAAoD,CACpD,iVAA4W,CAE9W,SAEE,gCAAkC,CAClC,eAAkB,CAEpB,WACE,kBAAqB,CACrB,iCAAsC,CACtC,wPAAmR,CAErR,smGACE,kBAAqB,CACrB,kCAAmC,CACnC,iCAAkC,CAClC,iBAAkB,CAClB,mBAAoB,CACpB,eAAmB,CACnB,oBAAqB,CACrB,mBAAsB,CAExB,4BACE,WAAc,CAEhB,6BACE,WAAc,CAEhB,8BACE,WAAc,CAEhB,6BACE,WAAc,CAEhB,sBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,sBACE,WAAc,CAEhB,8BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,sBACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,6BACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,oBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,6BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,6BACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,oBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,4BACE,WAAc,CAEhB,oBACE,WAAc,CAEhB,mBACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,mBACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,gCACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,qCACE,WAAc,CAEhB,iCACE,WAAc,CAEhB,uCACE,WAAc,CAEhB,wCACE,WAAc,CAEhB,gCACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,sBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,mCACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,8BACE,WAAc,CAEhB,uCACE,WAAc,CAEhB,sCACE,WAAc,CAEhB,gCACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,6BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,gCACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,+BACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,2BACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,wBACE,WAAc,CAEhB,yBACE,WAAc,CAEhB,0BACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,sBACE,WAAc,CAEhB,gCACE,WAAc,CAEhB,uBACE,WAAc,CAEhB,iCACE,WAAc,CAEhB,qBACE,WAAc,CAEhB,sBACE,6EAA8E,CAC9E,kEAAuE,CACvE,2DAA4D,CAC5D,mCAAoC,CACpC,4CAA6C,CAC7C,+EAAgF,CAChF,mCAAoC,CACpC,8EAAmF,CACnF,0CAA2C,CAC3C,+DAAgE,CAChE,iEAAkE,CAClE,kEAAmE,CACnE,gEAAiE,CACjE,oEAAqE,CACrE,sEAAuE,CACvE,uEAAwE,CACxE,qEAAsE,CACtE,yDAA0D,CAC1D,0DAA2D,CAC3D,uEAAwE,CACxE,wEAAyE,CACzE,uEAAwE,CACxE,yEAA0E,CAC1E,6EAA8E,CAC9E,4EAA6E,CAC7E,8EAA+E,CAC/E,mEAAoE,CACpE,uEAAwE,CACxE,wEAAyE,CACzE,yEAA0E,CAC1E,8EAA+E,CAC/E,iDAAkD,CAClD,6EAA8E,CAC9E,2EAA4E,CAC5E,gFAAiF,CACjF,wFAAyF,CACzF,6GAAgH,CAChH,8GAAiH,CACjH,8FAA+F,CAC/F,+EAAmF,CACnF,oFAAyF,CACzF,6DAA8D,CAC9D,sDAAuD,CACvD,kDAAmD,CACnD,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,8EAA+E,CAC/E,6EAA8E,CAC9E,0EAA2E,CAC3E,0EAA2E,CAC3E,+EAAgF,CAChF,uEAAwE,CACxE,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,4EAA6E,CAC7E,8EAA+E,CAC/E,+EAAgF,CAChF,6EAA8E,CAC9E,kCAAmC,CACnC,iBAAkB,CAClB,2CAA4C,CAC5C,YAAa,CACb,+CAAgD,CAChD,mEAAoE,CACpE,wCAAyC,CACzC,0CAA2C,CAC3C,iBAAkB,CAClB,eAAgB,CAChB,6DAA8D,CAC9D,iDAAoD,CACpD,oCACE,sBACE,8EAA+E,CAC/E,kFAAmF,CACnF,oFAAqF,CACrF,gFAAiF,CAGjF,gGAAiG,CACjG,8FAA+F,CAC/F,kGALmF,CAAE,CAMzF,yCACE,sBACE,gGAAiG,CACjG,kGAAqG,CAAE,CAC3G,yCACE,sBACE,gGAAmG,CAAE,CACzG,yCACE,sBACE,kGAAmG,CACnG,gGAAiG,CAGjG,oGAAqG,CAGrG,8FAA+F,CAC/F,kGAAmG,CACnG,oGAAqG,CACrG,gGAAiG,CAGjG,4EAA6E,CAC7E,qFAbmG,CAAE,CAczG,yCACE,sBACE,sEAAuE,CACvE,oEAAqE,CACrE,4EAA6E,CAC7E,+CAAgD,CAChD,mDAAoD,CACpD,qDAAwD,CAAE,CAEhE,6BACE,eAAgB,CAChB,YAAa,CACb,yMAA4M,CAE9M,mCACE,uDAA0D,CAE5D,8BACE,gBAAiB,CACjB,YAAa,CACb,qBAAsB,CACtB,+DAAgE,CAChE,iEAAkE,CAClE,6DAAgE,CAElE,iCACE,8DAA+D,CAC/D,eAAgB,CAChB,0DAA6D,CAE/D,+BACE,YAAa,CACb,qBAAsB,CACtB,iBAAkB,CAClB,4MAA6M,CAC7M,iBAAkB,CAClB,eAAgB,CAChB,2BAA4B,CAC5B,gCAAiC,CACjC,qBAAwB,CACxB,oCACE,+BACE,gBAAiB,CACjB,wBAA2B,CAAE,CAEnC,6BACE,eAAgB,CAChB,eAAgB,CAChB,KAAM,CACN,YAAa,CACb,sBAAuB,CACvB,wBAAyB,CACzB,0DAA2D,CAC3D,8DAA+D,CAC/D,gEAAmE,CACnE,yCACE,6BACE,aAAgB,CAChB,sBAAyB,CAAE,CAC/B,yCACE,6BACE,wBAA2B,CAAE,CACjC,qDACE,YAAa,CACb,kBAAmB,CACnB,sBAAuB,CACvB,yDAA0D,CAC1D,2DAA4D,CAC5D,gEAAiE,CACjE,yDAA0D,CAC1D,8EAA+E,CAC/E,wEAA2E,CAC3E,2DACE,6HAAgI,CAEtI,4BACE,YAAa,CACb,iBAAoB,CACpB,yCACE,4BACE,aAAc,CACd,kBAAmB,CACnB,uEAAwE,CACxE,2BAA4B,CAC5B,2BAA4B,CAC5B,6EAA8E,CAC9E,qEAAsE,CACtE,cAAiB,CAAE,CAEzB,gBACE,wEAAyE,CACzE,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,6DAA8D,CAC9D,uFAAwF,CACxF,uFAAwF,CACxF,wFAAyF,CACzF,yEAA0E,CAC1E,kGAAmG,CACnG,iFAAkF,CAClF,iFAAkF,CAClF,kFAAmF,CACnF,iGAAkG,CAClG,iFAAkF,CAClF,gGAAiG,CACjG,sFAAuF,CACvF,qGAAsG,CACtG,wDAAyD,CACzD,+DAAgE,CAChE,gFAAiF,CACjF,kFAAmF,CACnF,mFAAoF,CACpF,iFAAkF,CAClF,sEAAuE,CACvE,2EAA4E,CAC5E,4EAA6E,CAC7E,wFAAyF,CACzF,mIAAoI,CACpI,+DAAgE,CAChE,kCAAmC,CACnC,uDAA0D,CAE5D,wBACE,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,UAAW,CACX,qLAAsL,CACtL,QAAW,CACX,+BACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,kDAAmD,CACnD,UAAW,CACX,uEAA0E,CAC5E,sCACE,oHAAuH,CACvH,mEACE,8EAA+E,CAC/E,mEAAsE,CACxE,mEACE,gFAAmF,CACvF,8BACE,sEAAyE,CACzE,2DACE,8DAAiE,CACrE,8BACE,sEAAyE,CACzE,2DACE,yEAA0E,CAC1E,8DAAiE,CACrE,+BACE,uEAA0E,CAC1E,4DACE,0EAA2E,CAC3E,+DAAkE,CAExE,6BACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,sDAAyD,CAE3D,6BACE,yDAA4D,CAE9D,kCACE,2DAA4D,CAC5D,oDAAuD,CACvD,6CACE,sEAAuE,CACvE,eAAkB,CACpB,gDACE,oKAAuK,CAE3K,uCACE,iBAAkB,CAClB,iPAAoP,CACpP,8CACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,iEAAkE,CAClE,UAAW,CACX,sFAAyF,CAE7F,kBACE,oCAAqC,CACrC,mEAAoE,CACpE,oEAAuE,CAEzE,2CAEE,6EAA8E,CAC9E,6EAA8E,CAC9E,YAAa,CACb,kBAAqB,CACrB,mDAEE,kDAAqD,CACvD,kMAIE,kDAAqD,CACvD,iEAEE,yEAA4E,CAEhF,YACE,uDAAwD,CACxD,oEAAqE,CACrE,6DAA8D,CAC9D,8DAA+D,CAC/D,iEAAkE,CAClE,qDAAsD,CACtD,uDAAwD,CACxD,wDAAyD,CACzD,sDAAuD,CACvD,qDAAsD,CACtD,8DAA+D,CAC/D,uCAAwC,CACxC,4DAA6D,CAC7D,iEAAkE,CAClE,kEAAmE,CACnE,+DAAgE,CAChE,gCAAiC,CACjC,+EAAkF,CAClF,kFAAqF,CACrF,yCAA0C,CAC1C,uEAA0E,CAC1E,kEAAmE,CACnE,mEAAoE,CACpE,gFAAiF,CACjF,8FAA+F,CAC/F,4EAA6E,CAC7E,yEAA0E,CAC1E,0EAA2E,CAC3E,0EAA2E,CAC3E,uEAAwE,CACxE,wEAAyE,CACzE,4EAA6E,CAC7E,yEAA0E,CAC1E,0EAA2E,CAC3E,sEAAuE,CACvE,mEAAoE,CACpE,oEAAqE,CACrE,sCAAuC,CACvC,0EAA2E,CAC3E,sFAAuF,CACvF,mFAAoF,CACpF,qFAAsF,CACtF,kFAAmF,CACnF,kCAAmC,CACnC,iBAAkB,CAClB,YAAa,CACb,qIAAsI,CACtI,qCAAsC,CACtC,mDAAoD,CACpD,oFAAqF,CACrF,uCAAwC,CACxC,4DAA6D,CAC7D,+FAAkG,CAClG,yBACE,yEAA0E,CAC1E,mEAAoE,CACpE,qEAAsE,CACtE,+FAAkG,CACpG,wBACE,wEAAyE,CACzE,kEAAmE,CACnE,oEAAqE,CACrE,8FAAiG,CACnG,yBACE,yEAA0E,CAC1E,mEAAoE,CACpE,qEAAsE,CACtE,+FAAkG,CACpG,sBACE,sEAAuE,CACvE,gEAAiE,CACjE,kEAAmE,CACnE,4FAA+F,CACjG,wBACE,8DAA+D,CAC/D,0EAA6E,CAEjF,kBACE,cAAe,CACf,YAAa,CACb,6CAA8C,CAC9C,iDAAkD,CAClD,2CAA4C,CAC5C,oCAAuC,CAEzC,mBACE,eAAgB,CAChB,gDAAiD,CACjD,qCAAsC,CACtC,qBAAwB,CACxB,iCACE,mBAAoB,CACpB,2BAA4B,CAC5B,sDAAuD,CACvD,eAAkB,CAEtB,yBACE,qBAAsB,CACtB,sDAAuD,CACvD,qBAAwB,CACxB,mDACE,+FAAkG,CAEtG,oBACE,gBAAiB,CACjB,+CAAgD,CAChD,mDAAoD,CACpD,qDAAsD,CACtD,2DAA8D,CAC9D,iCACE,2BAA8B,CAElC,0BACE,qBAAsB,CACtB,uDAA0D,CAC1D,uCACE,2DAA8D,CAC9D,wDACE,mFAAsF,CAE5F,uCACE,oEAAuE,CAEzE,kBACE,gEAAiE,CACjE,8DAA+D,CAC/D,+DAAgE,CAChE,6CAA8C,CAC9C,iEAAoE,CACpE,sBACE,mDAAsD,CACxD,6BACE,cAAe,CACf,yCAA0C,CAC1C,6CAA8C,CAC9C,gDAAiD,CACjD,4DAAqE,CACrE,oDAAuD,CAE3D,mBACE,uFAAwF,CACxF,oEAAqE,CACrE,kEAAmE,CACnE,qEAAsE,CACtE,wEAAyE,CACzE,8DAA+D,CAC/D,uCAAwC,CACxC,uFAAwF,CACxF,8EAA+E,CAC/E,sEAAuE,CACvE,iFAAkF,CAClF,qEAAsE,CACtE,+DAAgE,CAChE,sEAAuE,CACvE,uEAAwE,CACxE,sEAAuE,CACvE,kFAAmF,CACnF,2EAA4E,CAC5E,yEAA0E,CAC1E,2EAA4E,CAC5E,4EAA6E,CAC7E,0EAA2E,CAC3E,sFAAuF,CACvF,qFAAsF,CACtF,2EAA4E,CAC5E,uEAAwE,CACxE,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,uEAAwE,CACxE,+EAAgF,CAChF,0CAA2C,CAC3C,iFAAkF,CAClF,mGAAoG,CACpG,sDAAuD,CACvD,0EAA2E,CAC3E,qFAAsF,CACtF,oDAAqD,CACrD,uFAAwF,CACxF,4EAA6E,CAC7E,8FAA+F,CAC/F,mFAAoF,CACpF,0GAA2G,CAC3G,6EAA8E,CAC9E,+EAAgF,CAChF,gFAAiF,CACjF,iFAAkF,CAClF,sFAAuF,CACvF,mEAAoE,CACpE,2FAA4F,CAC5F,0EAA2E,CAC3E,yEAA0E,CAC1E,gGAAiG,CACjG,kGAAmG,CACnG,8FAA+F,CAC/F,yEAA0E,CAC1E,oFAAqF,CACrF,yEAA0E,CAC1E,sEAAuE,CACvE,yEAA0E,CAC1E,iBAAkB,CAClB,oBAAqB,CACrB,cAAiB,CACjB,4DACE,yDAA4D,CAC9D,iCACE,yDAA0D,CAC1D,+DAAkE,CAClE,4CACE,8CAAiD,CAEvD,2BACE,iMAAkM,CAClM,6CAA8C,CAC9C,WAAc,CACd,iCACE,iFAAoF,CACtF,yEACE,kFAAqF,CACvF,iCACE,iFAAoF,CACtF,oCACE,oFAAqF,CACrF,mBAAsB,CAE1B,yBACE,iBAAkB,CAClB,uCAAwC,CACxC,8CAA+C,CAC/C,cAAe,CACf,sDAAuD,CACvD,4DAA6D,CAC7D,gEAAiE,CACjE,2BAA4B,CAC5B,oDAAuD,CACvD,0CACE,OAAU,CACZ,qDACE,yEAA0E,CAC1E,uEAA0E,CAE9E,gCACE,qNAAsN,CACtN,iEAAkE,CAClE,qIAAwI,CAE1I,iCACE,YAAe,CACf,+CACE,2HAA8H,CAElI,8BACE,YAAa,CACb,kBAAmB,CACnB,gDAAiD,CACjD,6MAA8M,CAC9M,2DAA4D,CAC5D,gDAAiD,CACjD,kBAAmB,CACnB,QAAW,CACX,wEACE,sHAAuH,CACvH,oBAAuB,CACzB,yMAGE,4EAA+E,CACjF,mFACE,0FAA2F,CAC3F,mBAAsB,CACxB,4KACE,4BAA+B,CACjC,8XACE,SAAY,CACd,wCACE,sGAAuG,CACvG,0HAA6H,CAC/H,0CACE,0FAA2F,CAC3F,0FAA2F,CAC3F,4HAA6H,CAC7H,iEAAoE,CACpE,gGACE,2GAA8G,CAEpH,mCACE,mBAAoB,CACpB,kBAAmB,CACnB,sBAAuB,CACvB,qDAAsD,CACtD,uDAAwD,CACxD,kEAAqE,CACrE,qCACE,cAAe,CACf,eAAkB,CAEtB,4CACE,2EAA4E,CAC5E,gBAAiB,CACjB,qEAAsE,CACtE,8DAA+D,CAC/D,SAAU,CACV,mFAAsF,CAExF,oDACE,8DAAiE,CAEnE,gCAIE,qNAAgE,CAChE,yDAA0D,CAC1D,6DAA8D,CAC9D,kDAAqD,CAEvD,aACE,8DAA+D,CAC/D,4BAA6B,CAC7B,6BAA8B,CAC9B,+BAAgC,CAChC,iCAAkC,CAClC,8CAAiD,CAEnD,eACE,oDAAqD,CACrD,wFAAyF,CACzF,cAAe,CACf,KAAM,CACN,MAAO,CACP,oCAAqC,CACrC,UAAW,CACX,WAAY,CACZ,sDAAyD,CAE3D,qBACE,eAAkB,CAEpB,uBACE,oFAAqF,CACrF,wEAA6E,CAC7E,8EAAmF,CACnF,4EAAiF,CACjF,kFAAuF,CACvF,6EAAkF,CAClF,mDAAwD,CACxD,8BACE,cAAe,CACf,KAAM,CACN,MAAO,CACP,UAAW,CACX,UAAW,CACX,WAAY,CACZ,UAAW,CACX,8DAA+D,CAC/D,8DAA+D,CAC/D,2CAA4C,CAC5C,2BAA4B,CAC5B,qBAAwB,CACxB,kEACE,8BACE,yFAA4F,CAAE,CAClG,yBACE,8BACE,0FAA6F,CAAE,CACnG,8GACE,8BACE,6FAAgG,CAAE,CACtG,yBACE,8BACE,0FAA6F,CAAE,CAEvG,+BACE,aAAgB,CAElB,YACE,6DAA8D,CAC9D,qDAAsD,CACtD,2DAA4D,CAC5D,uDAAwD,CACxD,sDAAuD,CACvD,qDAAsD,CACtD,mDAAoD,CACpD,4EAA6E,CAC7E,6DAA8D,CAC9D,4EAA6E,CAC7E,gEAAiE,CACjE,oBAAqB,CACrB,qCAAsC,CACtC,6CAA8C,CAC9C,2CAA4C,CAC5C,qCAAsC,CACtC,yCAA0C,CAC1C,8BAA+B,CAC/B,iBAAkB,CAClB,mDAAoD,CACpD,6CAAgD,CAChD,sBACE,oDAAqD,CACrD,wEAA2E,CAC7E,wBACE,sDAAuD,CACvD,0EAA6E,CAEjF,aACE,sDAAuD,CACvD,wDAAyD,CACzD,4DAA6D,CAC7D,yDAA0D,CAC1D,uDAAwD,CACxD,2DAA4D,CAC5D,sDAAuD,CACvD,iDAAkD,CAClD,0EAA2E,CAC3E,0EAA2E,CAC3E,4EAA6E,CAC7E,8EAA+E,CAC/E,8EAA+E,CAC/E,4DAA6D,CAC7D,yEAA0E,CAC1E,kCAAmC,CACnC,eAAgB,CAChB,sBAAuB,CAEvB,yIAA0I,CAC1I,aAAc,CACd,sCAAuC,CACvC,+BAAgC,CAChC,kBAAmB,CACnB,oDAAuD,CACvD,yBACE,aACE,gEAAiE,CACjE,8DAAiE,CAAE,CACvE,uBACE,kCAAmC,CACnC,0EAA6E,CAC/E,yBACE,4EAA+E,CACjF,0BACE,6EAAgF,CAClF,0BACE,kCAAmC,CACnC,6EAAgF,CAClF,yBACE,eAAgB,CAChB,KAAM,CACN,4CAA6C,CAC7C,kDAAqD,CAEzD,iBACE,gEAAiE,CACjE,oEAAqE,CACrE,iEAAkE,CAClE,yEAA0E,CAC1E,yEAA0E,CAC1E,wEAAyE,CACzE,sEAAuE,CACvE,mEAAoE,CACpE,mBAAsB,CAExB,uBACE,YAAa,CACb,cAAe,CACf,kBAAqB,CAEvB,uBACE,YAAa,CACb,oBAAqB,CACrB,gDAAiD,CACjD,oDAAqD,CACrD,oDAAqD,CACrD,kBAAmB,CACnB,eAAkB,CAClB,wCACE,sDAAyD,CAE7D,+BACE,8DAA+D,CAC/D,wDAAyD,CACzD,aAAc,CACd,iDAAoD,CAEtD,uBACE,iBAAkB,CAClB,oDAAqD,CACrD,mBAAoB,CACpB,qBAAwB,CACxB,oCACE,cAAiB,CACjB,8EACE,oDAAqD,CACrD,oBAAuB,CAE7B,0BACE,cAAe,CACf,mDAAsD,CAExD,iDAEE,kBAAqB,CAEvB,sFAEE,mDAAsD,CAExD,mEACE,YAAa,CACb,iBAAoB,CAEtB,aACE,gEAAiE,CACjE,wDAAyD,CACzD,mEAAoE,CACpE,uDAAwD,CACxD,0DAA2D,CAC3D,8DAA+D,CAC/D,sDAAuD,CACvD,8DAA+D,CAC/D,qEAAsE,CACtE,6CAA8C,CAC9C,mEAAoE,CACpE,0EAA2E,CAC3E,0EAA2E,CAC3E,2EAA4E,CAC5E,oEAAqE,CACrE,8EAA+E,CAC/E,uDAAwD,CACxD,8EAA+E,CAC/E,kEAAmE,CACnE,qFAAsF,CACtF,yEAA0E,CAC1E,qFAAsF,CACtF,yEAA0E,CAC1E,sFAAuF,CACvF,0EAA2E,CAC3E,uDAAwD,CACxD,mFAAoF,CACpF,sEAAuE,CACvE,8DAA+D,CAC/D,0FAA2F,CAC3F,6EAA8E,CAC9E,8DAA+D,CAC/D,0FAA2F,CAC3F,6EAA8E,CAC9E,+DAAgE,CAChE,2FAA4F,CAC5F,8EAA+E,CAC/E,sDAAuD,CACvD,0EAA2E,CAC3E,6DAA8D,CAC9D,6DAA8D,CAC9D,iFAAkF,CAClF,oEAAqE,CACrE,6DAA8D,CAC9D,iFAAkF,CAClF,oEAAqE,CACrE,8DAA+D,CAC/D,kFAAmF,CACnF,qEAAsE,CACtE,8EAA+E,CAC/E,iEAAkE,CAClE,oFAAqF,CACrF,wEAAyE,CACzE,oFAAqF,CACrF,wEAAyE,CACzE,qFAAsF,CACtF,yEAA0E,CAC1E,4EAA6E,CAC7E,iEAAkE,CAClE,mFAAoF,CACpF,wEAAyE,CACzE,mFAAoF,CACpF,wEAAyE,CACzE,oFAAqF,CACrF,yEAA0E,CAC1E,kDAAmD,CACnD,0DAA2D,CAC3D,yDAA0D,CAC1D,wEAAyE,CACzE,yDAA0D,CAC1D,wEAAyE,CACzE,0DAA2D,CAC3D,yEAA0E,CAC1E,4DAA6D,CAC7D,iDAAkD,CAClD,oGAAqG,CACrG,kFAAmF,CACnF,mDAAoD,CACpD,0DAA2D,CAC3D,0DAA2D,CAC3D,iEAAkE,CAClE,0DAA2D,CAC3D,iEAAkE,CAClE,2DAA4D,CAC5D,kEAAmE,CACnE,6EAA8E,CAC9E,6DAA8D,CAC9D,gFAAiF,CACjF,4DAA6D,CAC7D,wCAAyC,CACzC,8EAA+E,CAC/E,kFAAmF,CACnF,oFAAqF,CACrF,qFAAsF,CACtF,mFAAoF,CACpF,yFAA0F,CAC1F,uFAAwF,CACxF,mEAAoE,CACpE,2FAA4F,CAC5F,6FAA8F,CAC9F,wFAAyF,CACzF,oEAAqE,CACrE,4FAA6F,CAC7F,8FAA+F,CAC/F,uFAAwF,CACxF,mEAAoE,CACpE,2FAA4F,CAC5F,6FAA8F,CAC9F,4FAA6F,CAC7F,wEAAyE,CACzE,gGAAiG,CACjG,kGAAmG,CACnG,+DAAgE,CAChE,oEAAqE,CACrE,sEAAuE,CACvE,uEAAwE,CACxE,qEAAsE,CACtE,0EAA2E,CAC3E,4EAA6E,CAC7E,sEAAuE,CACvE,mEAAoE,CACpE,wGAAyG,CACzG,kCAAmC,CACnC,wCAAyC,CACzC,gCAAiC,CACjC,0DAA2D,CAC3D,qDAAsD,CACtD,kFAAmF,CACnF,kHAAqH,CACrH,iHAAoH,CACpH,uEAAwE,CACxE,kHAAmH,CACnH,iBAAkB,CAClB,oBAAqB,CACrB,yIAA0I,CAC1I,sCAAuC,CACvC,0CAA2C,CAC3C,0CAA2C,CAC3C,iBAAkB,CAClB,kBAAmB,CACnB,gBAAiB,CACjB,QAAS,CACT,8CAAiD,CACjD,mBACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,mBAAoB,CACpB,UAAW,CACX,mDAAoD,CACpD,mDAAoD,CACpD,qDAAwD,CAC1D,mBACE,+EAAgF,CAChF,oBAAuB,CACzB,mBACE,+EAAkF,CACpF,6CACE,gFAAmF,CACrF,wBACE,aAAc,CACd,UAAa,CACf,wBACE,6DAAgE,CAClE,wKACE,sEAAuE,CACvE,0EAA2E,CAC3E,4EAA6E,CAC7E,wEAAyE,CACzE,sEAAyE,CAC3E,0BACE,0CAA2C,CAC3C,+DAAkE,CAClE,gCACE,2EAA4E,CAC5E,+FAAkG,CACpG,gCACE,2EAA4E,CAC5E,+FAAkG,CACpG,uEACE,4EAA6E,CAC7E,gGAAmG,CACvG,4BACE,qFAAsF,CACtF,4CAA6C,CAC7C,iEAAoE,CACpE,kCACE,+EAAgF,CAChF,mGAAoG,CACpG,4FAA+F,CACjG,kCACE,+EAAgF,CAChF,mGAAoG,CACpG,4FAA+F,CACjG,2EACE,gFAAiF,CACjF,oGAAqG,CACrG,6FAAgG,CACpG,2BACE,oFAAqF,CACrF,2CAA4C,CAC5C,gEAAmE,CACnE,iCACE,6EAA8E,CAC9E,iGAAkG,CAClG,2FAA8F,CAChG,iCACE,6EAA8E,CAC9E,iGAAkG,CAClG,2FAA8F,CAChG,yEACE,8EAA+E,CAC/E,kGAAmG,CACnG,4FAA+F,CACnG,yBACE,yCAA0C,CAC1C,8DAAiE,CACjE,+BACE,yEAA0E,CAC1E,6FAAgG,CAClG,+BACE,yEAA0E,CAC1E,6FAAgG,CAClG,qEACE,0EAA2E,CAC3E,8FAAiG,CACrG,0BACE,0CAA2C,CAC3C,+DAAkE,CAClE,gCACE,2EAA4E,CAC5E,+FAAkG,CACpG,gCACE,2EAA4E,CAC5E,+FAAkG,CACpG,uEACE,4EAA6E,CAC7E,gGAAmG,CACvG,uBACE,8FAA+F,CAC/F,uCAAwC,CACxC,4DAA+D,CAC/D,+CACE,qEAAsE,CACtE,yFAA4F,CAC9F,+CACE,qEAAsE,CACtE,yFAA4F,CAC9F,qGACE,sEAAuE,CACvE,0FAA6F,CAC/F,mCACE,sEAAuE,CACvE,cAAe,CACf,SAAU,CACV,eAAgB,CAChB,kBAAmB,CACnB,cAAiB,CACjB,yCACE,+EAAgF,CAChF,2EAA8E,CAClF,uCACE,0EAA6E,CACjF,0BACE,uEAAwE,CACxE,iGAAkG,CAClG,mFAAoF,CACpF,2PAA4P,CAC5P,0CAA2C,CAC3C,+DAAkE,CAClE,gCACE,qBAAwB,CAC1B,gCACE,2EAA4E,CAC5E,+FAAgG,CAChG,iHAAoH,CACpH,sCACE,kFAAqF,CACzF,uEACE,4EAA6E,CAC7E,gGAAiG,CACjG,kHAAqH,CACrH,mFACE,mFAAsF,CAC1F,gCACE,2EAA4E,CAC5E,+FAAgG,CAChG,iHAAoH,CACpH,sCACE,kFAAqF,CACzF,wCACE,gFAAiF,CACjF,oGAAqG,CACrG,sHAAyH,CACzH,8CACE,uFAA0F,CAChG,wBACE,2EAA4E,CAC5E,+FAAgG,CAChG,wCAAyC,CACzC,6DAAgE,CAChE,8BACE,uEAAwE,CACxE,2FAA8F,CAChG,mEACE,wEAAyE,CACzE,4FAA+F,CACjG,8BACE,uEAAwE,CACxE,2FAA8F,CAClG,iDACE,mBAAsB,CACxB,iFACE,kFAAmF,CACnF,yCAA0C,CAC1C,8DAAiE,CACnE,gCACE,mCAAoC,CACpC,2DAA4D,CAC5D,cAAiB,CACnB,2BACE,wEAAyE,CACzE,sEAAuE,CACvE,gHAAmH,CACrH,8BACE,2EAA4E,CAC5E,yEAA4E,CAEhF,8BACE,2DAA8D,CAEhE,4BACE,uDAA0D,CAE5D,uBACE,iBAAkB,CAClB,qCAAsC,CACtC,uCAAwC,CACxC,aAAc,CACd,8DAAiE,CACjE,qCACE,kCAAqC,CAEzC,iCACE,iEAAoE,CAEtE,qBACE,6EAA8E,CAC9E,8DAA+D,CAC/D,gEAAiE,CACjE,iEAAkE,CAClE,+DAAgE,CAChE,8DAA+D,CAC/D,wEAAyE,CACzE,6CAA8C,CAC9C,wDAAyD,CACzD,uDAAwD,CACxD,iGAAkG,CAClG,yGAA4G,CAC5G,0GAA6G,CAC7G,gGAAiG,CACjG,gFAAiF,CACjF,iFAAkF,CAClF,sEAAuE,CACvE,2EAA4E,CAC5E,sDAAuD,CACvD,wDAAyD,CACzD,yDAA0D,CAC1D,uDAAwD,CACxD,kGAAmG,CACnG,0GAA2G,CAC3G,wGAAyG,CACzG,+GAAgH,CAChH,gHAAiH,CACjH,mHAAoH,CACpH,uHAAwH,CACxH,6FAA8F,CAC9F,sEAAuE,CACvE,gDAAiD,CACjD,kDAAmD,CACnD,uGAAwG,CACxG,iDAAkD,CAClD,yGAA0G,CAC1G,8EAA+E,CAC/E,6EAA8E,CAC9E,+GAAgH,CAChH,+GAAgH,CAChH,sGAAuG,CACvG,sCAAuC,CACvC,uCAAwC,CACxC,4EAA6E,CAC7E,+DAAgE,CAChE,wDAAyD,CACzD,kFAAmF,CACnF,iFAAkF,CAClF,2DAA4D,CAC5D,sFAAuF,CACvF,sFAAuF,CACvF,0FAA2F,CAC3F,kDAAmD,CACnD,kCAAmC,CACnC,mBAAoB,CACpB,qBAAsB,CACtB,yKAA0K,CAC1K,8CAA+C,CAC/C,4DAA+D,CAEjE,6BACE,YAAa,CACb,8DAAiE,CAEnE,yCACE,wEAAyE,CACzE,sEAAyE,CACzE,yDACE,gIAAiI,CACjI,8HAAiI,CACnI,yDACE,gIAAiI,CACjI,8HAAiI,CAErI,mCACE,WAAc,CAEhB,kCACE,oDAAuD,CAEzD,+BACE,kBAAqB,CAEvB,2BACE,2HAA8H,CAEhI,0BACE,6DAA8D,CAC9D,uDAAwD,CACxD,iBAAoB,CAEtB,4CACE,wHAA2H,CAE7H,iCACE,iGAAkG,CAClG,iBAAkB,CAClB,yNAA0N,CAC1N,iBAAoB,CACpB,wCACE,iBAAkB,CAClB,uDAAwD,CACxD,2DAA4D,CAC5D,6DAA8D,CAC9D,yDAA0D,CAC1D,UAAW,CACX,gFAAmF,CACrF,8CACE,qHAAwH,CAC1H,+CACE,sIAAuI,CACvI,oIAAqI,CACrI,oIAAuI,CACzI,kDACE,+HAAkI,CACpI,gDACE,+HAAkI,CACpI,qDACE,wGAA2G,CAC7G,+CACE,sHAAuH,CACvH,oIAAqI,CACrI,oIAAqI,CACrI,0IAA2I,CAC3I,uHAAwH,CACxH,kGAAqG,CACvG,+CACE,sEAAuE,CACvE,wDAA2D,CAE/D,2BACE,iBAAkB,CAClB,mBAAoB,CACpB,kBAAmB,CACnB,sBAAuB,CACvB,6CAA8C,CAC9C,+CAAgD,CAChD,aAAc,CACd,6CAA8C,CAC9C,kEAAmE,CACnE,QAAW,CACX,iCACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,sHAAyH,CAC3H,4DACE,4DAA+D,CACjE,uEACE,qGAAwG,CAC1G,uEACE,qGAAsG,CACtG,2GAA4G,CAC5G,SAAU,CACV,6DAAgE,CAClE,oCACE,mBAAoB,CACpB,oFAAqF,CACrF,kEAAqE,CAEzE,WACE,mEAAoE,CACpE,sDAAuD,CACvD,0EAA2E,CAC3E,2EAA4E,CAC5E,2EAA4E,CAC5E,4EAA6E,CAC7E,gFAAiF,CACjF,uFAAwF,CACxF,kGAAmG,CACnG,qEAAsE,CACtE,uEAAwE,CACxE,4EAA6E,CAC7E,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,iFAAkF,CAClF,0FAA2F,CAC3F,kEAAmE,CACnE,mEAAoE,CACpE,iEAAkE,CAClE,6DAA8D,CAC9D,8DAA+D,CAC/D,4DAA6D,CAC7D,sEAAuE,CACvE,qFAAwF,CACxF,oEAAqE,CACrE,wFAA2F,CAC3F,4EAA+E,CAC/E,wEAAyE,CACzE,yDAA0D,CAC1D,2DAA4D,CAC5D,iEAAkE,CAClE,+EAAgF,CAChF,0DAA2D,CAC3D,4DAA6D,CAC7D,8DAA+D,CAC/D,oEAAqE,CACrE,YAAa,CACb,qBAAsB,CACtB,kDAAmD,CACnD,sCAAyC,CACzC,gCACE,0DAA6D,CAC/D,2BACE,iBAAkB,CAClB,cAAiB,CACjB,iCACE,2DAA8D,CAChE,iCACE,2DAA8D,CAChE,kCACE,4DAA+D,CACjE,yCACE,gEAAmE,CACnE,gDACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,MAAO,CACP,iEAAkE,CAClE,UAAW,CACX,oFAAuF,CAC7F,wBACE,uEAAwE,CACxE,2EAA4E,CAC5E,yFAA0F,CAC1F,iFAAkF,CAClF,mFAAoF,CACpF,+EAAgF,CAChF,mGAAoG,CACpG,qHAAwH,CAC1H,qBACE,2BAA4B,CAC5B,wFAA2F,CAC7F,wDACE,0EAA6E,CAC/E,6KAIE,0DAA6D,CAEjE,mBACE,YAAa,CACb,kBAAmB,CACnB,kBAAqB,CACrB,qCACE,SAAY,CAEhB,0BACE,qBAAsB,CACtB,wLAA2L,CAE7L,+BACE,oBAAqB,CACrB,2DAA8D,CAEhE,kBACE,+CAAgD,CAChD,+CAAkD,CAEpD,oBACE,YAAa,CACb,kBAAmB,CACnB,qBAAsB,CACtB,OAAQ,CACR,mDAAoD,CACpD,iJAAoJ,CACpJ,wBACE,wDAA2D,CAC7D,kHAGE,SAAY,CAEhB,yEAIE,mDAAoD,CACpD,qDAAsD,CACtD,iDAAoD,CACpD,yHAIE,qDAAwD,CAE5D,uEAEE,sEAAyE,CAE3E,+BACE,sCAAyC,CAE3C,oCACE,aAAgB,CAElB,iBACE,0CAA6C,CAE/C,mBACE,4CAA+C,CAEjD,iDACE,gDAAmD,CAErD,YACE,+EAAgF,CAChF,0EAA2E,CAC3E,uDAAwD,CACxD,oEAAqE,CACrE,4DAA6D,CAC7D,gEAAiE,CACjE,yCAA0C,CAC1C,kEAAmE,CACnE,6DAA8D,CAC9D,YAAa,CACb,8BAA+B,CAC/B,mCAAoC,CACpC,kBAAmB,CACnB,mBAAsB,CAExB,mBACE,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAiD,CACjD,qCAAwC,CAE1C,mBACE,8CAAiD,CAEnD,yBACE,aAAc,CACd,kDAAmD,CACnD,2CAA8C,CAEhD,uDAEE,cAAiB,CAEnB,0HAGE,oEAAqE,CACrE,kBAAqB,CAEvB,WACE,oDAAqD,CACrD,sDAAuD,CACvD,uDAAwD,CACxD,qDAAsD,CACtD,+DAAgE,CAChE,4DAA6D,CAC7D,mEAAoE,CACpE,kEAAmE,CACnE,gEAAiE,CACjE,yEAA0E,CAC1E,gFAAiF,CACjF,mEAAoE,CACpE,6EAA8E,CAC9E,0DAA2D,CAC3D,qDAAsD,CACtD,gCAAiC,CACjC,8DAA+D,CAC/D,gEAAiE,CACjE,iEAAkE,CAClE,+DAAgE,CAChE,sEAAyE,CACzE,4EAAiF,CACjF,4EAA+E,CAC/E,8DAA+D,CAC/D,6DAA8D,CAC9D,0DAA2D,CAC3D,kCAAmC,CACnC,iBAAkB,CAClB,mBAAoB,CACpB,kBAAmB,CACnB,iIAAkI,CAClI,eAAgB,CAChB,kDAAmD,CACnD,4CAA+C,CAC/C,kBACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,wFAAyF,CACzF,oDAAuD,CACzD,yBACE,QAAW,CACX,0CACE,+CAAkD,CACtD,0BACE,2EAA4E,CAC5E,mDAAsD,CACtD,2CACE,uDAA0D,CAC9D,wBACE,gEAAiE,CACjE,oEAAqE,CACrE,sEAAuE,CACvE,kEAAmE,CACnE,4DAA6D,CAC7D,gDAAiD,CACjD,oDAAqD,CACrD,sDAAyD,CAC3D,uBACE,iDAAoD,CAExD,iBACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,iBAAkB,CAClB,0CAA2C,CAC3C,0CAA2C,CAC3C,mCAAsC,CAExC,oEAEE,8CAAiD,CAEnD,iBACE,kCAAmC,CACnC,2EAA8E,CAC9E,0EAA6E,CAC7E,sEAAuE,CACvE,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,8EAA+E,CAC/E,qFAAsF,CACtF,kEAAmE,CACnE,iEAAkE,CAClE,uCAAwC,CACxC,yEAA4E,CAC5E,4EAA+E,CAC/E,sEAAuE,CACvE,uEAA0E,CAC1E,+BAIE,yMAA6D,CAC7D,oEAAqE,CACrE,8DAAiE,CAErE,uBACE,YAAa,CACb,MAAO,CACP,cAAe,CACf,oBAAuB,CAEzB,uBACE,sDAAuD,CACvD,wDAA2D,CAE7D,wCAEE,mBAAoB,CACpB,cAAe,CACf,kBAAqB,CAEvB,4BACE,mBAAoB,CACpB,2DAA4D,CAC5D,6DAAgE,CAElE,wBACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,iDAAkD,CAClD,uDAAwD,CACxD,iDAAoD,CAEtD,wBACE,YAAa,CACb,qBAAsB,CACtB,mDAAoD,CACpD,yDAA4D,CAE9D,qBACE,6DAA8D,CAC9D,4DAA6D,CAC7D,kFAAmF,CACnF,oFAAqF,CACrF,qFAAsF,CACtF,mFAAoF,CACpF,uGAAwG,CACxG,2DAA4D,CAC5D,6FAA8F,CAC9F,8FAA+F,CAC/F,4FAA6F,CAC7F,yFAA0F,CAC1F,8FAAmG,CACnG,qEACE,6EAAgF,CAEpF,4BACE,YAAe,CACf,gCACE,gBAAmB,CAEvB,kCACE,8DAAiE,CAEnE,yCACE,yPAA0P,CAC1P,oBAAqB,CACrB,gFAAiF,CACjF,2BAA4B,CAC5B,wEAAyE,CACzE,8QAA+Q,CAC/Q,oEAAuE,CACvE,6CACE,oBAAuB,CAE3B,kBACE,qFAAsF,CACtF,4FAA6F,CAC7F,4FAA6F,CAC7F,wHAAyH,CACzH,uFAAwF,CACxF,wFAAyF,CACzF,wEAAyE,CACzE,uEAAwE,CACxE,gFAAiF,CACjF,4FAA6F,CAC7F,6FAA8F,CAC9F,gGAAiG,CACjG,mGAAoG,CACpG,yDAA0D,CAC1D,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,qEAAsE,CACtE,gFAAiF,CACjF,+EAAgF,CAChF,2DAA4D,CAC5D,0EAA2E,CAC3E,kEAAmE,CACnE,6EAA8E,CAC9E,iEAAkE,CAClE,yEAA0E,CAC1E,2EAA4E,CAC5E,4CAA6C,CAC7C,0EAA2E,CAC3E,uEAAwE,CACxE,2EAA8E,CAC9E,iCACE,qGAAwG,CAE5G,0BACE,iBAAkB,CAClB,YAAa,CACb,oBAAuB,CACvB,iCACE,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,MAAO,CACP,mBAAoB,CACpB,UAAW,CACX,yIAA4I,CAEhJ,4BACE,YAAe,CACf,sDACE,6FAAgG,CAChG,4DACE,6HAAgI,CAClI,4DACE,6HAAgI,CAClI,qEACE,8GAAiH,CAEvH,wBACE,iBAAkB,CAClB,+DAAgE,CAChE,uDAAwD,CACxD,uDAA0D,CAC1D,8CACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,oFAAqF,CACrF,mEAAsE,CACxE,+CACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,8IAAiJ,CACnJ,uCACE,+DAAkE,CAEtE,kDACE,kBAAqB,CAEvB,wBACE,iBAAkB,CAClB,qLAAwL,CACxL,oDACE,yDAA0D,CAC1D,qDAAsD,CACtD,oBAAuB,CAE3B,uBACE,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CACnB,iLAAkL,CAClL,gBAAiB,CACjB,yCAA0C,CAC1C,8DAA+D,CAG/D,yGAAuM,CAAvM,6GAAuM,CAAvM,2GAAuM,CAAvM,uGAAyM,CAE3M,wDACE,+DAAkE,CAEpE,cACE,yDAA0D,CAC1D,2DAA4D,CAC5D,uDAAwD,CACxD,+DAAgE,CAChE,kDAAmD,CACnD,qFAAsF,CACtF,0DAA2D,CAC3D,6DAA8D,CAC9D,+DAAgE,CAChE,4DAA6D,CAC7D,mEAAoE,CACpE,0DAA2D,CAC3D,6DAA8D,CAC9D,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,0DAA2D,CAC3D,6DAA8D,CAC9D,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,0DAA2D,CAC3D,6DAA8D,CAC9D,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,0DAA2D,CAC3D,6DAA8D,CAC9D,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,0DAA2D,CAC3D,6DAA8D,CAC9D,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,gEAAiE,CACjE,kEAAmE,CACnE,8DAA+D,CAC/D,yDAA0D,CAC1D,sDAAuD,CACvD,wEAAyE,CACzE,oEAAqE,CACrE,sFAAuF,CACvF,mEAAoE,CACpE,qEAAsE,CACtE,sEAAuE,CACvE,oEAAqE,CACrE,8DAA+D,CAC/D,8EAA+E,CAC/E,6EAA8E,CAC9E,4DAA6D,CAC7D,2DAA4D,CAC5D,kEAAmE,CACnE,mEAAoE,CACpE,4DAA6D,CAC7D,2DAA4D,CAC5D,kEAAmE,CACnE,mEAAoE,CACpE,yDAA0D,CAC1D,0DAA2D,CAC3D,2DAA4D,CAC5D,uDAAwD,CACxD,sEAAuE,CACvE,0DAA2D,CAC3D,mCAAoC,CACpC,4DAA6D,CAC7D,sEAAuE,CACvE,uCAAwC,CACxC,2CAA4C,CAC5C,gCAAmC,CACnC,gBACE,mCAAoC,CACpC,sDAAyD,CACzD,sBACE,6DAA8D,CAC9D,+EAAkF,CACtF,oBACE,6CAAgD,CAClD,gUASE,+CAAkD,CACpD,sGAME,QAAS,CACT,oDAAuD,CACvD,8KAME,YAAe,CACjB,wKAME,eAAkB,CACtB,kCAEE,QAAW,CACb,iBACE,6CAA8C,CAC9C,mDAAoD,CACpD,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,iBACE,6CAA8C,CAC9C,mDAAoD,CACpD,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,iBACE,6CAA8C,CAC9C,mDAAoD,CACpD,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,iBACE,6CAA8C,CAC9C,mDAAoD,CACpD,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,iBACE,6CAA8C,CAC9C,mDAAoD,CACpD,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,iBACE,6CAA8C,CAC9C,mDAAoD,CACpD,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,oBACE,aAAc,CACd,8CAA+C,CAC/C,kDAAmD,CACnD,uCAA0C,CAC1C,qCACE,sDAAyD,CAC7D,yBACE,6LAA8L,CAC9L,4CAA6C,CAC7C,mHAAsH,CACxH,iBACE,sCAAuC,CACvC,yDAA0D,CAC1D,WAAc,CAChB,iBACE,iDAAkD,CAClD,+CAAkD,CAClD,oBACE,qDAAsD,CACtD,0EAA6E,CAC/E,oBACE,qDAAsD,CACtD,0EAA6E,CACjF,iBACE,iDAAkD,CAClD,+CAAgD,CAChD,6CAAgD,CAChD,oBACE,qDAAsD,CACtD,0EAA6E,CAC/E,oBACE,qDAAsD,CACtD,0EAA6E,CACjF,iBACE,YAAa,CACb,yBAA4B,CAC5B,oCACE,iBACE,2BAA8B,CAC9B,kDAAmD,CACnD,4CAA+C,CAAE,CACvD,iBACE,+CAAkD,CAClD,mCACE,6CAAgD,CAChD,oCACE,mCACE,oEAAuE,CAAE,CAC/E,oCACE,iBACE,aAAgB,CAAE,CACxB,oCACE,iBACE,aAAgB,CAAE,CAExB,kCACE,+DAAgE,CAChE,sEAAuE,CACvE,sEAAuE,CACvE,sEAAyE,CACzE,6CACE,+CAAkD,CAEtD,uBACE,wCAAyC,CACzC,kFAAmF,CACnF,0EAA2E,CAC3E,qFAAsF,CACtF,yEAA0E,CAC1E,8EAA+E,CAC/E,kFAAmF,CACnF,oFAAqF,CACrF,qFAAsF,CACtF,mFAAoF,CACpF,mEAAoE,CACpE,6FAA8F,CAC9F,4FAA6F,CAC7F,8FAA+F,CAC/F,8FAA+F,CAC/F,gGAAiG,CACjG,6EAA8E,CAC9E,qFAAsF,CACtF,iFAAkF,CAClF,8EAA+E,CAC/E,6EAA8E,CAC9E,4EAA6E,CAC7E,kEAAmE,CACnE,sEAAuE,CACvE,2FAA4F,CAC5F,wEAAyE,CACzE,6EAA8E,CAC9E,+EAAgF,CAChF,gFAAiF,CACjF,8EAA+E,CAC/E,0FAA2F,CAC3F,yFAA0F,CAC1F,mFAAoF,CACpF,6EAA8E,CAC9E,+EAAgF,CAChF,gFAAiF,CACjF,8EAA+E,CAC/E,qDAAsD,CACtD,gFAAiF,CACjF,kFAAmF,CACnF,mFAAoF,CACpF,iFAAkF,CAClF,4GAA6G,CAC7G,0FAA2F,CAC3F,iBAAkB,CAClB,oBAAqB,CACrB,yCAA0C,CAC1C,cAAiB,CAEnB,+BACE,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,UAAW,CACX,iNAAkN,CAClN,iDAAkD,CAClD,kBAAmB,CACnB,cAAe,CACf,WAAc,CACd,sCACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,8DAA+D,CAC/D,sOAAyO,CAC3O,4CACE,iHAAoH,CACtH,kJACE,kHAAmH,CACnH,mFAAsF,CACxF,qDACE,oHAAqH,CACrH,qFAAwF,CAC1F,mEACE,mEAAoE,CACpE,iEAAoE,CACtE,mEACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,6DAA8D,CAC9D,iEAAkE,CAClE,iEAAoE,CAExE,6BACE,kCAAmC,CACnC,iBAAkB,CAClB,2CAA4C,CAC5C,kDAAmD,CACnD,cAAe,CACf,0DAA2D,CAC3D,oEAAqE,CACrE,2BAA4B,CAC5B,wDAA2D,CAE7D,oCACE,iBAAkB,CAClB,qOAAsO,CACtO,6IAAgJ,CAElJ,oCACE,qOAAsO,CACtO,gBAAiB,CACjB,+DAAkE,CAEpE,kCACE,6DAA8D,CAC9D,iBAAoB,CAEtB,uCACE,YAAa,CACb,kBAAmB,CACnB,UAAW,CACX,iPAAkP,CAClP,kBAAmB,CACnB,WAAc,CACd,0FACE,oBAAqB,CACrB,qFAAwF,CAC1F,gDACE,mEAAoE,CACpE,mBAAsB,CAE1B,oCACE,wCACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,2DACE,wCACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,oCACI,sEACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,+FACE,qDAAwD,CAC1D,oFACE,MAAO,CACP,OAAgB,CAClB,+EACE,gBAAmB,CACrB,2EACE,WAAc,CAChB,0EACE,WAAc,CAChB,0EACE,WAAc,CAChB,0EACE,WAAc,CAChB,0EACE,WAAc,CAChB,4EACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,gCACE,+BACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,uDACE,+BACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,gCACI,6DACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,sFACE,qDAAwD,CAC1D,2EACE,MAAO,CACP,OAAgB,CAClB,sEACE,gBAAmB,CACrB,kEACE,WAAc,CAChB,iEACE,WAAc,CAChB,iEACE,WAAc,CAChB,iEACE,WAAc,CAChB,iEACE,WAAc,CAChB,mEACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,oCACE,6BACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,2DACE,6BACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,oCACI,2DACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,oFACE,qDAAwD,CAC1D,yEACE,MAAO,CACP,OAAgB,CAClB,oEACE,gBAAmB,CACrB,gEACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,iEACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,oCACE,6BACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,2DACE,6BACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,oCACI,2DACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,oFACE,qDAAwD,CAC1D,yEACE,MAAO,CACP,OAAgB,CAClB,oEACE,gBAAmB,CACrB,gEACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,iEACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,oCACE,6BACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,2DACE,6BACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,oCACI,2DACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,oFACE,qDAAwD,CAC1D,yEACE,MAAO,CACP,OAAgB,CAClB,oEACE,gBAAmB,CACrB,gEACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,iEACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,qCACE,6BACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,4DACE,6BACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,qCACI,2DACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,oFACE,qDAAwD,CAC1D,yEACE,MAAO,CACP,OAAgB,CAClB,oEACE,gBAAmB,CACrB,gEACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,+DACE,WAAc,CAChB,iEACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,qCACE,8BACE,0FAA2F,CAC3F,oFAAqF,CACrF,gGAAiG,CACjG,4FAA6F,CAC7F,oHAAqH,CACrH,0HAA2H,CAC3H,0GAA2G,CAC3G,8GAAiH,CAAE,CACrH,4DACE,8BACE,0FAA2F,CAC3F,wFAAyF,CACzF,wHAAyH,CACzH,sHAAyH,CAAE,CAEjI,qCACI,4DACE,YAAa,CACb,cAAe,CACf,WAAY,CACZ,qEAAwE,CAC1E,qFACE,qDAAwD,CAC1D,0EACE,MAAO,CACP,OAAgB,CAClB,qEACE,gBAAmB,CACrB,iEACE,WAAc,CAChB,gEACE,WAAc,CAChB,gEACE,WAAc,CAChB,gEACE,WAAc,CAChB,gEACE,WAAc,CAChB,kEACE,eAAmB,CACnB,kBAAqB,CAAE,CAE7B,gBACE,mEAAoE,CACpE,6DAA8D,CAC9D,sEAAuE,CACvE,uEAAwE,CACxE,8EAA+E,CAC/E,uEAAwE,CACxE,+FAAgG,CAChG,+FAAgG,CAChG,wHAA0H,CAC1H,yFAA4F,CAC5F,6GAA8G,CAC9G,iIAAmI,CACnI,iIAAmI,CACnI,kIAAoI,CACpI,6GAA8G,CAC9G,4EAA6E,CAC7E,gDAAiD,CACjD,gHAAiH,CACjH,gHAAiH,CACjH,+EAAgF,CAChF,gFAAiF,CACjF,2DAA4D,CAC5D,uEAAwE,CACxE,uEAAwE,CACxE,qCAAsC,CACtC,+FAAkG,CAClG,qEAAsE,CACtE,oEAAqE,CACrE,yEAA0E,CAC1E,wEAAyE,CACzE,8EAA+E,CAC/E,+DAAgE,CAChE,kEAAmE,CACnE,gEAAiE,CACjE,2CAA4C,CAC5C,wEAAyE,CACzE,0CAA2C,CAC3C,yEAA0E,CAC1E,6EAA8E,CAC9E,wCAAyC,CACzC,wCAAyC,CACzC,yCAA0C,CAC1C,yCAA0C,CAC1C,wCAAyC,CACzC,gDAAiD,CACjD,0EAA6E,CAC7E,mFAAsF,CACtF,wDAAyD,CACzD,6DAA8D,CAC9D,iFAAkF,CAClF,kFAAmF,CACnF,wFAA2F,CAC3F,yFAA4F,CAC5F,wFAAyF,CACzF,gHAAiH,CACjH,yGAA0G,CAC1G,yGAA0G,CAC1G,kGAAmG,CACnG,sDAAuD,CACvD,uEAAwE,CACxE,0EAA2E,CAC3E,wEAAyE,CACzE,4EAA6E,CAC7E,wFAAyF,CACzF,2CAA4C,CAC5C,sEAAuE,CACvE,yEAA0E,CAC1E,sEAAuE,CACvE,0EAA2E,CAC3E,uFAAwF,CACxF,mFAAsF,CACtF,sFAAuF,CACvF,uFAAwF,CACxF,uHAA0H,CAC1H,qHAAwH,CACxH,uDAAwD,CACxD,yGAA4G,CAC5G,kFAAmF,CACnF,oFAAqF,CACrF,qFAAsF,CACtF,mFAAoF,CACpF,sFAAuF,CACvF,wFAAyF,CACzF,yFAA0F,CAC1F,uFAAwF,CACxF,oEAAqE,CACrE,2EAA4E,CAC5E,0EAA2E,CAC3E,6EAA8E,CAC9E,sDAAuD,CACvD,oDAAqD,CACrD,mFAAoF,CACpF,iFAAkF,CAClF,kFAAmF,CACnF,0DAA2D,CAC3D,mFAAoF,CACpF,iFAAkF,CAClF,oFAAqF,CACrF,iFAAkF,CAClF,qFAAsF,CACtF,kCAAmC,CACnC,wBAAyB,CACzB,oBAAqB,CACrB,4FAA+F,CAC/F,oCACE,gBACE,0EAA2E,CAC3E,0EAA2E,CAG3E,4FAA6F,CAC7F,4FAJ6E,CAAE,CAKnF,yBACE,gBACE,gFAAmF,CAAE,CACzF,6BACE,oDAAqD,CACrD,mGAAoG,CACpG,mGAAoG,CACpG,yGAA0G,CAC1G,uGAAwG,CACxG,qGAAsG,CACtG,2GAA4G,CAC5G,+GAAkH,CAClH,mDACE,qFAAsF,CACtF,2FAA4F,CAC5F,6FAA8F,CAC9F,gGAAmG,CACrG,oDACE,2DAA8D,CAClE,+BACE,oBAAuB,CAE3B,8IAIE,kFAAmF,CACnF,uCAAwC,CACxC,6CAA8C,CAC9C,yCAA4C,CAE9C,sJAIE,4CAA+C,CAEjD,sIAIE,yCAA4C,CAE9C,sBACE,iBAAkB,CAClB,YAAa,CACb,qBAAsB,CACtB,6DAA8D,CAC9D,iHAAoH,CACpH,6BACE,iBAAkB,CAClB,4CAA6C,CAC7C,QAAS,CACT,MAAO,CACP,gDAAiD,CACjD,UAAW,CACX,qEAAsE,CACtE,0DAA6D,CAC/D,sCACE,cAAe,CACf,uEAA0E,CAC1E,wFACE,iBAAkB,CAClB,gEAAmE,CACnE,kKACE,2CAA8C,CAC9C,8MACE,8JAAiK,CACvK,4CACE,sEAAyE,CAC3E,4CACE,sEAAyE,CAC3E,6CACE,uEAA0E,CAC9E,oCACE,gHAAiH,CACjH,iBAAkB,CAClB,uDAAwD,CACxD,6DAAgE,CAClE,2CACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,iFAAkF,CAClF,gEAAmE,CACrE,oCACE,gHAAmH,CACnH,wEACE,8HAAiI,CAEvI,0BACE,YAAa,CACb,gBAAiB,CACjB,2DAA4D,CAC5D,yDAA4D,CAE9D,8BACE,YAAa,CACb,gBAAiB,CACjB,2DAA4D,CAC5D,iEAAkE,CAClE,6DAAgE,CAChE,gDACE,6EAAgF,CAEpF,uCACE,wEAAyE,CACzE,sEAAuE,CACvE,kEAAmE,CACnE,oEAAqE,CACrE,QAAW,CACX,6CACE,8HAA+H,CAC/H,WAAc,CAChB,6CACE,8HAAiI,CACnI,8CACE,eAAkB,CACpB,qDACE,mIAAoI,CACpI,mBAAoB,CACpB,WAAc,CAChB,4EACE,8DAAiE,CAErE,6BACE,iFAAkF,CAClF,sBAAuB,CACvB,wBAAyB,CACzB,0DAA2D,CAC3D,gEAAiE,CACjE,0DAA6D,CAC7D,+CACE,4EAA+E,CACjF,qDACE,mDAAsD,CAE1D,wBACE,mDAAoD,CACpD,qDAAwD,CAE1D,6BACE,mBAAoB,CACpB,yDAA4D,CAC5D,iEACE,8EAAiF,CAErF,8BACE,YAAa,CACb,UAAW,CACX,8BAAiC,CAEnC,sBACE,MAAO,CACP,gBAAmB,CACnB,mDAAoD,CACpD,yDAA4D,CAC5D,4CACE,aAAc,CACd,OAAQ,CACR,yDAA4D,CAC9D,gCACE,WAAY,CACZ,6DAA8D,CAC9D,eAAoB,CACtB,sDACE,eAAkB,CAClB,iEAAoE,CACtE,uCACE,aAAgB,CAEpB,sBACE,oBAAuB,CAEzB,4CAEE,+CAAgD,CAChD,cAAe,CACf,8CAA+C,CAC/C,uDAAwD,CACxD,iDAAkD,CAClD,mDAAsD,CAExD,oCACE,+DAAgE,CAChE,eAAgB,CAChB,oIAAuI,CACvI,6EACE,yPAA4P,CAC5P,6FACE,SAAY,CAElB,uBACE,4DAA6D,CAC7D,+DAAgE,CAChE,qDAAsD,CACtD,uDAAwD,CACxD,sKAAwK,CACxK,mEAAoE,CACpE,sEAAuE,CACvE,wDAAyD,CACzD,+CAAgD,CAChD,4EAA6E,CAC7E,sEAAuE,CACvE,uDAAwD,CACxD,2EAA6E,CAC7E,0KAA2K,CAC3K,8DAA+D,CAC/D,uEAAwE,CACxE,wIAAyI,CACzI,YAAa,CACb,oBAAqB,CACrB,4CAA6C,CAC7C,kDAAmD,CACnD,uEAA0E,CAC1E,oCACE,uBACE,8DAA+D,CAC/D,8DAAiE,CAAE,CACvE,uCACE,0HAA2H,CAC3H,4FAA+F,CAC/F,yBACE,uCACE,0JAA8J,CAAE,CACpK,yBACE,uCACE,wNAA6N,CAAE,CACnO,0BACE,uCACE,sRAA4R,CAAE,CAClS,0BACE,uCACE,qVAA4V,CAAE,CACpW,wCACE,mBAAsB,CACxB,+CACE,2EAA+E,CACjF,qCACE,6HAAgI,CAChI,gHAAmH,CACnH,yBACE,qCACE,6KAAiL,CAAE,CACvL,yBACE,qCACE,0OAA+O,CAAE,CACrP,0BACE,qCACE,uSAA6S,CAAE,CACnT,0BACE,qCACE,qWAA4W,CAAE,CAEtX,8BACE,YAAa,CACb,2DAA4D,CAC5D,mDAAoD,CACpD,yDAA0D,CAC1D,8EAA+E,CAC/E,oBAAuB,CAEzB,iEAEE,eAAkB,CAEpB,6BACE,sDAAuD,CACvD,0DAA6D,CAC7D,0DACE,cAAiB,CAErB,kCACE,qHAAwH,CAE1H,kCACE,qHAAwH,CAE1H,kCACE,qHAAwH,CAE1H,yBACE,wCACE,qHAAwH,CAC1H,wCACE,qHAAwH,CAC1H,wCACE,qHAAwH,CAAE,CAE9H,yBACE,wCACE,qHAAwH,CAC1H,wCACE,qHAAwH,CAC1H,wCACE,qHAAwH,CAAE,CAE9H,0BACE,wCACE,qHAAwH,CAC1H,wCACE,qHAAwH,CAC1H,wCACE,qHAAwH,CAAE,CAE9H,0BACE,yCACE,qHAAwH,CAC1H,yCACE,qHAAwH,CAC1H,yCACE,qHAAwH,CAAE,CAE9H,yBACE,uDAAwD,CACxD,qDAAsD,CACtD,uDAAwD,CACxD,mDAAoD,CACpD,sDAAuD,CACvD,mEAAoE,CACpE,iEAAkE,CAClE,mEAAoE,CACpE,+DAAgE,CAChE,yEAA0E,CAC1E,2EAA4E,CAC5E,4EAA6E,CAC7E,oFAAqF,CACrF,2EAA4E,CAC5E,+FAAgG,CAChG,8EAA+E,CAC/E,+EAAgF,CAChF,kDAAmD,CACnD,gDAAiD,CACjD,wEAAyE,CACzE,0EAA2E,CAC3E,2EAA4E,CAC5E,yEAA0E,CAC1E,wEAAyE,CACzE,4DAA6D,CAC7D,oGAAqG,CACrG,2GAA4G,CAC5G,yGAA0G,CAC1G,4DAA6D,CAC7D,gKAAiK,CACjK,4IAA6I,CAC7I,wEAAyE,CACzE,4FAA6F,CAC7F,gGAAiG,CACjG,4EAA6E,CAC7E,+EAAgF,CAChF,0EAA2E,CAC3E,8EAA+E,CAC/E,6EAA8E,CAC9E,+EAAgF,CAChF,iFAAkF,CAClF,kFAAmF,CACnF,gFAAiF,CACjF,uFAA0F,CAC1F,0FAA6F,CAC7F,0DAA2D,CAC3D,oEAAqE,CACrE,+EAAgF,CAChF,8EAA+E,CAC/E,uGAAwG,CACxG,qDAAsD,CACtD,gFAAiF,CACjF,oFAAqF,CACrF,oGAAqG,CACrG,YAAa,CACb,4IAA6I,CAC7I,6UAAgV,CAChV,sCAA0C,CAE5C,+BACE,gBAAmB,CACnB,2CACE,sGAAuG,CACvG,oGAAqG,CACrG,sGAAuG,CACvG,kGAAqG,CAEzG,iCACE,0DAA2D,CAC3D,kEAAqE,CAEvE,qCACE,kEAAqE,CAEvE,gCACE,YAAa,CACb,yDAA0D,CAC1D,iEAAoE,CAEtE,uCACE,WAAc,CAEhB,wCACE,YAAe,CACf,+EACE,mFAAsF,CAE1F,iCACE,YAAa,CACb,0DAA2D,CAC3D,kEAAqE,CAEvE,sCACE,WAAY,CACZ,+DAAgE,CAChE,wDAA2D,CAE7D,+BACE,wDAAyD,CACzD,0DAA2D,CAC3D,0DAA2D,CAC3D,aAAc,CACd,gHAAmH,CAErH,+BACE,YAAa,CACb,qBAAwB,CACxB,8DACE,mDAAoD,CACpD,sDAAyD,CACzD,oGACE,iBAAkB,CAClB,KAAM,CACN,kEAAmE,CACnE,yFAA4F,CAElG,oDACE,4GAA+G,CAEjH,iFACE,mIAAsI,CAExI,8DAEE,YAAe,CAEjB,mEAEE,eAAkB,CAEpB,+BACE,iBAAkB,CAClB,UAAW,CACX,iNAAkN,CAClN,wDAAyD,CACzD,eAAgB,CAChB,cAAe,CACf,sEAAuE,CACvE,QAAW,CACX,qCACE,6GAAgH,CAClH,4CACE,oHAAuH,CACzH,6CACE,kHAAqH,CACrH,iFACE,yGAA0G,CAC1G,8EAAiF,CACrF,0CACE,wEAA2E,CAC7E,oEACE,kEAAqE,CACrE,0FACE,4GAA+G,CAErH,oCACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,WAAY,CACZ,sDAAyD,CAE3D,mCACE,4DAA6D,CAC7D,iBAAkB,CAClB,oEAAqE,CACrE,kEAAqE,CAEvE,oCACE,YAAa,CACb,WAAc,CAEhB,sCACE,6OAA8O,CAC9O,iEAAkE,CAClE,uEAA0E,CAE5E,qCACE,YAAa,CACb,kBAAmB,CACnB,oEAAuE,CAEzE,2CACE,oBAAqB,CACrB,oEAAqE,CACrE,iBAAkB,CAClB,uEAAwE,CACxE,0EAA6E,CAE/E,wEACE,mKAAsK,CACtK,0GAA6G,CAE/G,4GACE,mKAAsK,CACtK,0GAA6G,CAE/G,gJACE,mKAAsK,CACtK,0GAA6G,CAE/G,oLACE,mKAAsK,CACtK,0GAA6G,CAE/G,wNACE,mKAAsK,CACtK,0GAA6G,CAE/G,4PACE,mKAAsK,CACtK,0GAA6G,CAE/G,gSACE,mKAAsK,CACtK,0GAA6G,CAE/G,oUACE,mKAAsK,CACtK,0GAA6G,CAE/G,wWACE,mKAAsK,CACtK,0GAA6G,CAE/G,4YACE,oKAAuK,CACvK,0GAA6G,CAE/G,cACE,sEAAuE,CACvE,mDAAoD,CACpD,uDAAwD,CACxD,0DAA2D,CAC3D,kEAAmE,CACnE,iEAAkE,CAClE,iEAAkE,CAClE,qEAAsE,CACtE,0EAA2E,CAC3E,2FAA4F,CAC5F,8EAA+E,CAC/E,yFAA0F,CAC1F,sDAAuD,CACvD,uDAAwD,CACxD,qDAAsD,CACtD,uEAAwE,CACxE,oFAAqF,CACrF,wFAAyF,CACzF,uFAAwF,CACxF,wFAA2F,CAC3F,qFAAsF,CACtF,yDAA0D,CAC1D,8DAA+D,CAC/D,+DAAgE,CAChE,0EAA2E,CAC3E,wFAAyF,CACzF,qFAAsF,CACtF,yDAA0D,CAC1D,gFAAiF,CACjF,+EAAgF,CAChF,gFAAiF,CACjF,oDAAqD,CACrD,+EAAgF,CAChF,wEAAyE,CACzE,yCAA0C,CAC1C,wEAAyE,CACzE,6EAA8E,CAC9E,0EAA2E,CAC3E,uEAAwE,CACxE,uEAAwE,CACxE,4EAA6E,CAC7E,yFAA0F,CAC1F,+EAAgF,CAChF,iBAAkB,CAClB,mCAAoC,CACpC,YAAa,CACb,2CAA4C,CAC5C,iDAAkD,CAClD,qDAAwD,CACxD,oCACE,cACE,0GAA2G,CAC3G,4GAA6G,CAC7G,wGAA2G,CAAE,CACjH,qCACE,cACE,kFAAqF,CAAE,CAC3F,+BACE,+EAAgF,CAChF,8EAAiF,CAErF,gFAEE,yEAA4E,CAE9E,4GAEE,wCAA2C,CAC3C,kIAEE,wBAA2B,CAE/B,qBACE,yDAA0D,CAC1D,YAAa,CACb,kBAAmB,CACnB,wCAA2C,CAC3C,uCACE,yEAA4E,CAC5E,yCACE,8EAAiF,CACrF,4CACE,8EAAiF,CACjF,8CACE,mFAAsF,CAC1F,uCACE,yEAA4E,CAC5E,yCACE,8EAAiF,CACnF,2CACE,gBAAmB,CACvB,uCACE,yEAA4E,CAC5E,uHAEE,YAAa,CACb,iBAAoB,CACtB,6DACE,oBAAqB,CACrB,kBAAqB,CACzB,gCACE,wBAA2B,CAE/B,oBACE,wDAAyD,CACzD,wCAA2C,CAC3C,uCACE,yEAA4E,CAC9E,qCACE,uEAA0E,CAC5E,kDACE,oHAAuH,CACzH,uCACE,yEAA4E,CAC9E,oCACE,sEAAyE,CAC3E,+BACE,iEAAkE,CAClE,0DAA6D,CAC/D,oCACE,gBAAmB,CACnB,qDACE,gBAAmB,CACvB,+BACE,wBAA2B,CAE/B,+BACE,oBAAqB,CACrB,2DAA4D,CAC5D,8DAAiE,CAEnE,sDAEE,YAAa,CACb,cAAe,CACf,kBAAqB,CAEvB,uBACE,iBAAkB,CAClB,wDAAyD,CACzD,sDAAyD,CAE3D,+BACE,UAAa,CAEf,kCACE,iBAAkB,CAClB,QAAS,CACT,OAAQ,CACR,MAAO,CACP,uDAAwD,CACxD,YAAa,CACb,UAAW,CACX,6NAA8N,CAC9N,iBAAkB,CAClB,yEAA0E,CAC1E,6DAAgE,CAChE,oCACE,kCACE,eAAgB,CAChB,eAAkB,CAAE,CACxB,gDACE,YAAa,CACb,4EAA6E,CAC7E,kBAAqB,CACvB,6GAEE,wBAA2B,CAC7B,uDACE,YAAa,CACb,4EAA+E,CACjF,8CACE,YAAa,CACb,iBAAoB,CAExB,oFAEE,YAAa,CACb,cAAe,CACf,oBAAqB,CACrB,kEAAmE,CACnE,cAAiB,CACjB,4HAEE,wDAAyD,CACzD,wEAA2E,CAC7E,8HAEE,yDAA0D,CAC1D,YAAa,CACb,cAAe,CACf,cAAiB,CACnB,sSAIE,wBAA2B,CAE/B,0CACE,gCAAmC,CAErC,6CACE,+CAAkD,CAEpD,4DACE,uEAA0E,CAE5E,6BACE,iFAAoF,CACpF,mGAEE,YAAa,CACb,aAAc,CACd,kBAAqB,CACvB,mDACE,YAAa,CACb,iBAAoB,CAExB,yBACE,mCACE,iFAAoF,CACpF,+GAEE,YAAa,CACb,aAAc,CACd,kBAAqB,CACvB,yDACE,YAAa,CACb,iBAAoB,CAAE,CAE5B,yBACE,mCACE,iFAAoF,CACpF,+GAEE,YAAa,CACb,aAAc,CACd,kBAAqB,CACvB,yDACE,YAAa,CACb,iBAAoB,CAAE,CAE5B,yBACE,mCACE,iFAAoF,CACpF,+GAEE,YAAa,CACb,aAAc,CACd,kBAAqB,CACvB,yDACE,YAAa,CACb,iBAAoB,CAAE,CAE5B,0BACE,mCACE,iFAAoF,CACpF,+GAEE,YAAa,CACb,aAAc,CACd,kBAAqB,CACvB,yDACE,YAAa,CACb,iBAAoB,CAAE,CAE5B,0BACE,oCACE,iFAAoF,CACpF,iHAEE,YAAa,CACb,aAAc,CACd,kBAAqB,CACvB,0DACE,YAAa,CACb,iBAAoB,CAAE,CAE5B,uGAEE,gBAAmB,CAKrB,8OAEE,aAAgB,CAChB,uIAEE,gBAAmB,CAEvB,2BACE,YAAa,CACb,iBAAoB,CAEtB,4BACE,YAAa,CACb,kBAAqB,CAEvB,wGAEE,gBAAmB,CAErB,oGAEE,cAAiB,CAEnB,yBACE,mHAEE,gBAAmB,CAIrB,sQAEE,aAAgB,CAChB,mJAEE,gBAAmB,CACvB,iCACE,YAAa,CACb,iBAAoB,CACtB,kCACE,YAAa,CACb,kBAAqB,CACvB,oHAEE,gBAAmB,CACrB,gHAEE,cAAiB,CAAE,CAEvB,yBACE,mHAEE,gBAAmB,CAIrB,sQAEE,aAAgB,CAChB,mJAEE,gBAAmB,CACvB,iCACE,YAAa,CACb,iBAAoB,CACtB,kCACE,YAAa,CACb,kBAAqB,CACvB,oHAEE,gBAAmB,CACrB,gHAEE,cAAiB,CAAE,CAEvB,yBACE,mHAEE,gBAAmB,CAIrB,sQAEE,aAAgB,CAChB,mJAEE,gBAAmB,CACvB,iCACE,YAAa,CACb,iBAAoB,CACtB,kCACE,YAAa,CACb,kBAAqB,CACvB,oHAEE,gBAAmB,CACrB,gHAEE,cAAiB,CAAE,CAEvB,0BACE,mHAEE,gBAAmB,CAIrB,sQAEE,aAAgB,CAChB,mJAEE,gBAAmB,CACvB,iCACE,YAAa,CACb,iBAAoB,CACtB,kCACE,YAAa,CACb,kBAAqB,CACvB,oHAEE,gBAAmB,CACrB,gHAEE,cAAiB,CAAE,CAEvB,0BACE,qHAEE,gBAAmB,CAIrB,0QAEE,aAAgB,CAChB,qJAEE,gBAAmB,CACvB,kCACE,YAAa,CACb,iBAAoB,CACtB,mCACE,YAAa,CACb,kBAAqB,CACvB,sHAEE,gBAAmB,CACrB,kHAEE,cAAiB,CAAE,CAKvB,wFACE,wBAA2B,CAE7B,qCACE,mDAAsD,CAExD,+CACE,wBAA2B,CAE7B,qCACE,mDAAsD,CAExD,+CACE,wBAA2B,CAE7B,qCACE,mDAAsD,CAExD,+CACE,wBAA2B,CAE7B,yBAGE,oGACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAAE,CAEjC,yBAGE,oGACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAAE,CAEjC,yBAGE,oGACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAAE,CAEjC,0BAGE,oGACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAC7B,2CACE,mDAAsD,CACxD,qDACE,wBAA2B,CAAE,CAEjC,0BAGE,sGACE,wBAA2B,CAC7B,4CACE,mDAAsD,CACxD,sDACE,wBAA2B,CAC7B,4CACE,mDAAsD,CACxD,sDACE,wBAA2B,CAC7B,4CACE,mDAAsD,CACxD,sDACE,wBAA2B,CAAE,CAI/B,2EACE,wBAA2B,CAI7B,uEACE,mDAAsD,CAIxD,uEACE,mDAAsD,CAIxD,uEACE,mDAAsD,CAE1D,yBAGI,uFACE,wBAA2B,CAG7B,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAAE,CAE9D,yBAGI,uFACE,wBAA2B,CAG7B,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAAE,CAE9D,yBAGI,uFACE,wBAA2B,CAG7B,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAAE,CAE9D,0BAGI,uFACE,wBAA2B,CAG7B,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAGxD,mFACE,mDAAsD,CAAE,CAE9D,0BAGI,yFACE,wBAA2B,CAG7B,qFACE,mDAAsD,CAGxD,qFACE,mDAAsD,CAGxD,qFACE,mDAAsD,CAAE,CAE9D,8BACE,uBAEkE,CAEpE,0DAHE,gEAAiE,CACjE,+DAKkE,CAHpE,4BACE,kDAEkE,CAEpE,4BACE,kDAEkE,CAEpE,wDAHE,gEAAiE,CACjE,+DAKkE,CAHpE,4BACE,kDAEkE,CAEpE,4BACE,kDAEkE,CAEpE,yDAHE,gEAAiE,CACjE,+DAKkE,CAHpE,6BACE,mDAEkE,CAEpE,yBACE,oCACE,uBAEkE,CACpE,sEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,oEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,qEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,mCACE,mDAEkE,CAAE,CAExE,yBACE,oCACE,uBAEkE,CACpE,sEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,oEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,qEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,mCACE,mDAEkE,CAAE,CAExE,yBACE,oCACE,uBAEkE,CACpE,sEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,oEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,qEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,mCACE,mDAEkE,CAAE,CAExE,0BACE,oCACE,uBAEkE,CACpE,sEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,oEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,kCACE,kDAEkE,CACpE,kCACE,kDAEkE,CACpE,qEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,mCACE,mDAEkE,CAAE,CAExE,0BACE,qCACE,uBAEkE,CACpE,wEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,mCACE,kDAEkE,CACpE,mCACE,kDAEkE,CACpE,sEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,mCACE,kDAEkE,CACpE,mCACE,kDAEkE,CACpE,uEAFE,gEAAiE,CACjE,+DAIkE,CAHpE,oCACE,mDAEkE,CAAE,CAExE,2CACE,wBAA2B,CAE7B,kBACE,0CAA2C,CAC3C,0FAA2F,CAC3F,uEAAwE,CACxE,wEAAyE,CACzE,mEAAoE,CACpE,mFAAoF,CACpF,mLAAsL,CACtL,uHAAwH,CACxH,yDAA0D,CAC1D,0FAA2F,CAC3F,uEAAwE,CACxE,iEAAkE,CAClE,2EAA4E,CAC5E,wCAAyC,CACzC,oCAAqC,CACrC,oDAAqD,CACrD,sDAAuD,CACvD,iBAAkB,CAClB,oBAAuB,CAEzB,+BACE,0DAA2D,CAC3D,wDAAyD,CACzD,iDAAoD,CACpD,0CACE,2FAA8F,CAElG,4CACE,2DAA8D,CAEhE,4BACE,iBAAkB,CAClB,0CAA2C,CAC3C,8CAA+C,CAC/C,4CAA6C,CAC7C,iDAAkD,CAClD,mEAAoE,CACpE,uDAA0D,CAC1D,6CACE,2FAA4F,CAC5F,yFAA4F,CAC9F,uDACE,+EAAgF,CAChF,0EAA6E,CAEjF,cACE,wDAAyD,CACzD,kEAAmE,CACnE,yDAA0D,CAC1D,2EAA4E,CAC5E,qCAAsC,CACtC,+BAAgC,CAChC,iDAAkD,CAClD,0EAA2E,CAC3E,YAAa,CACb,kBAAmB,CACnB,kBAAmB,CACnB,sBAAuB,CACvB,UAAW,CACX,QAAW,CACX,oBACE,4FAAqG,CACrG,kBAAmB,CACnB,yCAA0C,CAC1C,UAAW,CACX,4DAA6D,CAC7D,mBAAsB,CACxB,4BACE,mBAAoB,CACpB,qBAAsB,CACtB,UAAW,CACX,cAAe,CACf,eAAgB,CAChB,eAAkB,CAClB,kCACE,sGAAuG,CACvG,mDAAsD,CAC1D,8BACE,+BAAkC,CACpC,4BACE,yDAA4D,CAC9D,4BACE,yDAA4D,CAC9D,4BACE,yDAA4D,CAC9D,4BACE,yDAA4D,CAC9D,4BACE,yDAA4D,CAC9D,6BACE,0DAA6D,CAC/D,6BACE,0DAA6D,CAC/D,yBACE,oCACE,+BAAkC,CACpC,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,mCACE,0DAA6D,CAC/D,mCACE,0DAA6D,CAAE,CACnE,yBACE,oCACE,+BAAkC,CACpC,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,mCACE,0DAA6D,CAC/D,mCACE,0DAA6D,CAAE,CACnE,yBACE,oCACE,+BAAkC,CACpC,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,mCACE,0DAA6D,CAC/D,mCACE,0DAA6D,CAAE,CACnE,0BACE,oCACE,+BAAkC,CACpC,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,kCACE,yDAA4D,CAC9D,mCACE,0DAA6D,CAC/D,mCACE,0DAA6D,CAAE,CACnE,0BACE,qCACE,+BAAkC,CACpC,mCACE,yDAA4D,CAC9D,mCACE,yDAA4D,CAC9D,mCACE,yDAA4D,CAC9D,mCACE,yDAA4D,CAC9D,mCACE,yDAA4D,CAC9D,oCACE,0DAA6D,CAC/D,oCACE,0DAA6D,CAAE,CAErE,aACE,8EAA+E,CAC/E,sCAAuC,CACvC,8EAA+E,CAC/E,2DAA4D,CAC5D,oCAAqC,CACrC,uCAAwC,CACxC,kCAAmC,CACnC,oCAAqC,CACrC,uDAAwD,CACxD,4CAA6C,CAC7C,6CAA8C,CAC9C,4DAA6D,CAC7D,4DAA6D,CAC7D,yDAA0D,CAC1D,4EAA6E,CAC7E,6EAA8E,CAC9E,+EAAmF,CACnF,8FAA+F,CAC/F,6GAA8G,CAC9G,kGAAmG,CACnG,6DAA8D,CAC9D,+DAAgE,CAChE,gEAAiE,CACjE,8DAA+D,CAC/D,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,wEAAyE,CACzE,0EAA2E,CAC3E,2EAA4E,CAC5E,yEAA0E,CAC1E,4EAA6E,CAC7E,8EAA+E,CAC/E,+EAAgF,CAChF,6EAA8E,CAC9E,2CAA4C,CAC5C,6CAA8C,CAC9C,8CAA+C,CAC/C,4CAA6C,CAC7C,8BAA+B,CAC/B,mCAAoC,CACpC,iCAAkC,CAClC,+BAAgC,CAChC,yCAA0C,CAC1C,mCAAoC,CACpC,+EAAgF,CAChF,0CAA2C,CAC3C,gDAAiD,CACjD,oDAAqD,CACrD,sDAAuD,CACvD,kEAAmE,CACnE,8EAA+E,CAC/E,oFAAqF,CACrF,gDAAiD,CACjD,wGAAyG,CACzG,mDAAoD,CACpD,iDAAkD,CAClD,qHAAsH,CACtH,yHAA0H,CAC1H,6DAA8D,CAC9D,kDAAmD,CACnD,kHAAmH,CACnH,iHAAkH,CAClH,kEAAmE,CACnE,iEAAkE,CAClE,gIAAiI,CACjI,uCAAwC,CACxC,wGAAyG,CACzG,sDAAuD,CACvD,uHAAwH,CACxH,+EAAgF,CAChF,uFAAwF,CACxF,yDAA0D,CAC1D,0FAA2F,CAC3F,wDAAyD,CACzD,gGAAiG,CACjG,gGAAiG,CACjG,6EAA8E,CAC9E,+GAAgH,CAChH,gFAAiF,CACjF,8GAA+G,CAC/G,oDAAqD,CACrD,qDAAsD,CACtD,0EAA2E,CAC3E,2EAA4E,CAC5E,+EAAkF,CAClF,iFAAoF,CACpF,oCAAqC,CACrC,gFAAiF,CACjF,+FAAgG,CAChG,+FAAgG,CAChG,oEAAqE,CACrE,qFAAsF,CACtF,wDAAyD,CACzD,qGAAsG,CACtG,mFAAoF,CACpF,kGAAmG,CACnG,kGAAmG,CACnG,YAAa,CACb,qBAAsB,CACtB,WAAY,CACZ,iBAAoB,CACpB,oCACE,aACE,wEAAyE,CACzE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gGAAiG,CACjG,oGAAqG,CACrG,sGAAuG,CACvG,kGAAqG,CAAE,CAC3G,qCACE,aACE,wEAAyE,CACzE,sEAAyE,CACzE,+BACE,mCAAoC,CACpC,wFAAyF,CACzF,wFAA2F,CAAE,CACnG,gIACE,6DAAgE,CAClE,oEACE,OAAQ,CACR,0DAA6D,CAC7D,2BAA8B,CAChC,sEACE,OAAU,CACZ,kDACE,qBAAwB,CAC1B,kEACE,2BAA8B,CAChC,kFACE,uBAA0B,CAC5B,oFACE,2BAAgC,CAEpC,sBACE,WAAY,CACZ,6DAAgE,CAChE,yCACE,4BAA+B,CAEnC,mBACE,YAAa,CACb,WAAY,CACZ,eAAkB,CAEpB,0CAEE,YAAa,CACb,qBAAsB,CACtB,aAAc,CACd,aAAgB,CAElB,sBACE,2CAA4C,CAC5C,iDAAkD,CAClD,OAAQ,CACR,6DAAgE,CAChE,yCACE,yMAA4M,CAC9M,yCACE,4BAA+B,CAEnC,oBACE,iBAAkB,CAClB,yCAA0C,CAC1C,+CAAgD,CAChD,OAAQ,CACR,+CAAgD,CAChD,aAAc,CACd,2DAA4D,CAC5D,+CAAgD,CAChD,iEAAkE,CAClE,iEAAkE,CAClE,gCAAmC,CACnC,0BACE,iBAAkB,CAClB,KAAM,CACN,MAAO,CACP,6CAA8C,CAC9C,WAAY,CACZ,UAAW,CACX,kEAAqE,CACvE,uCACE,4BAA+B,CAEnC,+BACE,GACE,iBAAoB,CAAE,CAE1B,4BACE,kCAAmC,CACnC,6DAA8D,CAC9D,4BAA+B,CAEjC,mBACE,YAAa,CACb,0BAA2B,CAC3B,6BAAgC,CAChC,qBACE,aAAgB,CAEpB,sBACE,aAAc,CACd,UAAW,CACX,YAAa,CACb,mBAAoB,CACpB,iDAAkD,CAClD,qDAAwD,CAE1D,mBACE,YAAa,CACb,qKAAwK,CACxK,mCACE,SAAY,CACZ,qIAEE,YAAa,CACb,cAAiB,CACrB,gCACE,iNAAoN,CACtN,2CACE,aAAgB,CAClB,8BACE,QAAW,CAEf,oCACE,eAAkB,CAEpB,uBACE,iBAAkB,CAClB,qCAAsC,CACtC,yCAA0C,CAC1C,2CAA4C,CAC5C,uCAAwC,CACxC,YAAa,CACb,yCAA0C,CAC1C,2CAA4C,CAC5C,2CAA4C,CAC5C,iBAAkB,CAClB,8DAAiE,CACjE,qCACE,gFAAiF,CACjF,8EAA+E,CAC/E,gFAAiF,CACjF,oHAAqH,CACrH,sHAAuH,CACvH,sIAAuI,CACvI,0IAA2I,CAC3I,4IAA6I,CAC7I,wIAA2I,CAC7I,6BACE,2HAA8H,CAChI,6BACE,2HAA8H,CAChI,6BACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,6DAA8D,CAC9D,kOAAqO,CAEzO,8BACE,iBAAkB,CAClB,4CAA6C,CAC7C,8CAA+C,CAC/C,8BAAkC,CAClC,oCACE,aAAc,CACd,uDAAwD,CACxD,yDAA0D,CAC1D,UAAW,CAGX,qIAA+P,CAA/P,yIAA+P,CAA/P,uIAA+P,CAA/P,mIAAiQ,CAErQ,oCACE,aACE,6CAAgD,CAChD,kEACE,2DAA8D,CAChE,mEACE,gEAAmE,CACnE,yEACE,OAAQ,CACR,QAAW,CACjB,6BACE,0GAA6G,CAC7G,gKACE,6EAA8E,CAC9E,cAAiB,CACnB,kFACE,uBAA0B,CAC5B,0EACE,OAAQ,CACR,SAAY,CACd,mFACE,gFAAiF,CACjF,cAAiB,CACjB,0GACE,gCAAiC,CACjC,kCAAmC,CACnC,4FAA6F,CAC7F,kDAAmD,CACnD,kHAAqH,CAC3H,+BACE,4GAA6G,CAC7G,oCAAqC,CACrC,cAAe,CACf,mEAAsE,CACtE,oKACE,2EAA4E,CAC5E,cAAiB,CACnB,4EACE,KAAM,CACN,SAAU,CACV,UAAW,CACX,+DAAkE,CACpE,qFACE,8EAA+E,CAC/E,cAAiB,CACjB,4GACE,8BAA+B,CAC/B,gCAAiC,CACjC,oCAAqC,CACrC,4FAA6F,CAC7F,kDAAmD,CACnD,wHAA2H,CACjI,sGACE,oGAAqG,CACrG,wFAAyF,CACzF,gHAAiH,CACjH,8GAA+G,CAC/G,2EAA8E,CAChF,wHACE,8FAA+F,CAC/F,sGAAuG,CACvG,4HAA6H,CAC7H,kDAAmD,CACnD,iDAAoD,CACtD,sJAEE,gDAAmD,CACrD,uBACE,aAAc,CACd,kBAAqB,CAAE,CAE3B,yBACE,kCACE,mCAAsC,CACxC,kCACE,mCAAsC,CACxC,kCACE,mCAAsC,CACxC,kCACE,mCAAsC,CACxC,kCACE,mCAAsC,CACxC,mCACE,oCAAuC,CAAE,CAE7C,yBACE,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,yCACE,oCAAuC,CAAE,CAE7C,0BACE,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,wCACE,mCAAsC,CACxC,yCACE,oCAAuC,CAAE,CAE7C,0BACE,yCACE,mCAAsC,CACxC,yCACE,mCAAsC,CACxC,yCACE,mCAAsC,CACxC,yCACE,mCAAsC,CACxC,yCACE,mCAAsC,CACxC,0CACE,oCAAuC,CAAE,CAE7C,yBACE,oIAEE,aAAgB,CAClB,gIAEE,gDAAmD,CACnD,sLAEE,wFAA2F,CAC/F,kEACE,eAAkB,CACpB,gEACE,yDAA4D,CAC5D,0BAA6B,CAC/B,8EACE,aAAc,CACd,uBAA0B,CAC5B,0HACE,aAAc,CACd,kBAAqB,CACvB,gFACE,0DAA6D,CAC7D,aAAc,CACd,2BAA8B,CAChC,8FACE,cAAe,CACf,uBAA0B,CAC5B,0IACE,aAAc,CACd,kBAAqB,CACvB,gEACE,uBAA0B,CAC5B,gFACE,cAAe,CACf,uBAA0B,CAC5B,kFACE,uBAA0B,CAC5B,0HACE,YAAa,CACb,iBAAoB,CAAE,CAE1B,yBACE,gJAEE,aAAgB,CAClB,4IAEE,gDAAmD,CACnD,kMAEE,wFAA2F,CAC/F,wEACE,eAAkB,CACpB,sEACE,yDAA4D,CAC5D,0BAA6B,CAC/B,oFACE,aAAc,CACd,uBAA0B,CAC5B,gIACE,aAAc,CACd,kBAAqB,CACvB,sFACE,0DAA6D,CAC7D,aAAc,CACd,2BAA8B,CAChC,oGACE,cAAe,CACf,uBAA0B,CAC5B,gJACE,aAAc,CACd,kBAAqB,CACvB,sEACE,uBAA0B,CAC5B,sFACE,cAAe,CACf,uBAA0B,CAC5B,wFACE,uBAA0B,CAC5B,gIACE,YAAa,CACb,iBAAoB,CAAE,CAE1B,0BACE,gJAEE,aAAgB,CAClB,4IAEE,gDAAmD,CACnD,kMAEE,wFAA2F,CAC/F,wEACE,eAAkB,CACpB,sEACE,yDAA4D,CAC5D,0BAA6B,CAC/B,oFACE,aAAc,CACd,uBAA0B,CAC5B,gIACE,aAAc,CACd,kBAAqB,CACvB,sFACE,0DAA6D,CAC7D,aAAc,CACd,2BAA8B,CAChC,oGACE,cAAe,CACf,uBAA0B,CAC5B,gJACE,aAAc,CACd,kBAAqB,CACvB,sEACE,uBAA0B,CAC5B,sFACE,cAAe,CACf,uBAA0B,CAC5B,wFACE,uBAA0B,CAC5B,gIACE,YAAa,CACb,iBAAoB,CAAE,CAE1B,0BACE,kJAEE,aAAgB,CAClB,8IAEE,gDAAmD,CACnD,oMAEE,wFAA2F,CAC/F,yEACE,eAAkB,CACpB,uEACE,yDAA4D,CAC5D,0BAA6B,CAC/B,qFACE,aAAc,CACd,uBAA0B,CAC5B,iIACE,aAAc,CACd,kBAAqB,CACvB,uFACE,0DAA6D,CAC7D,aAAc,CACd,2BAA8B,CAChC,qGACE,cAAe,CACf,uBAA0B,CAC5B,iJACE,aAAc,CACd,kBAAqB,CACvB,uEACE,uBAA0B,CAC5B,uFACE,cAAe,CACf,uBAA0B,CAC5B,yFACE,uBAA0B,CAC5B,iIACE,YAAa,CACb,iBAAoB,CAAE,CAE1B,eACE,0EAA2E,CAC3E,kEAAmE,CACnE,6EAA8E,CAC9E,iEAAkE,CAClE,yEAA0E,CAC1E,gEAAiE,CACjE,wEAAyE,CACzE,2DAA4D,CAC5D,oEAAqE,CACrE,oDAAqD,CACrD,8EAA+E,CAC/E,kFAAmF,CACnF,oFAAqF,CACrF,qFAAsF,CACtF,mFAAoF,CACpF,6FAA8F,CAC9F,4FAA6F,CAC7F,8FAA+F,CAC/F,2FAA4F,CAC5F,6FAA8F,CAC9F,gGAAiG,CACjG,kGAAmG,CACnG,wFAAyF,CACzF,oEAAqE,CACrE,2EAA4E,CAC5E,uFAAwF,CACxF,0DAA2D,CAC3D,4EAA6E,CAC7E,mFAAoF,CACpF,wFAAyF,CACzF,+FAAgG,CAChG,gGAAiG,CACjG,+FAAgG,CAChG,oGAAqG,CACrG,kEAAmE,CACnE,iGAAkG,CAClG,yFAA0F,CAC1F,oGAAqG,CACrG,wFAAyF,CACzF,2EAA4E,CAC5E,8FAA+F,CAC/F,8FAA+F,CAC/F,kGAAmG,CACnG,mGAAoG,CACpG,wHAA2H,CAC3H,mFAAoF,CACpF,6FAA8F,CAC9F,yEAA0E,CAC1E,sEAAuE,CACvE,qEAAsE,CACtE,8DAA+D,CAC/D,mFAAoF,CACpF,gEAAiE,CACjE,8DAA+D,CAC/D,iEAAkE,CAClE,oEAAqE,CACrE,0DAA2D,CAC3D,mCAAoC,CACpC,mFAAoF,CACpF,uDAAwD,CACxD,mEAAoE,CACpE,qEAAsE,CACtE,sEAAuE,CACvE,oEAAqE,CACrE,mEAAoE,CACpE,2EAA4E,CAC5E,uEAAwE,CACxE,mEAAoE,CACpE,0EAA2E,CAC3E,6EAA8E,CAC9E,+FAAgG,CAChG,iEAAkE,CAClE,2EAA4E,CAC5E,yEAA0E,CAC1E,2EAA4E,CAC5E,4EAA6E,CAC7E,+EAAgF,CAChF,+EAAgF,CAChF,sEAAuE,CACvE,qEAAsE,CACtE,wFAAyF,CACzF,0FAA2F,CAC3F,sFAAuF,CACvF,qEAAsE,CACtE,gFAAiF,CACjF,qEAAsE,CACtE,qEAAsE,CACtE,wEAAyE,CACzE,uEAAwE,CACxE,kEAAmE,CACnE,qEAAsE,CACtE,iBAAkB,CAClB,oBAAqB,CACrB,cAAiB,CACjB,6BACE,qDAAsD,CACtD,2DAA8D,CAC9D,wCACE,0CAA6C,CAEnD,uBACE,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,gDAAiD,CACjD,cAAe,CACf,iLAAkL,CAClL,gDAAiD,CACjD,oDAAqD,CACrD,oDAAqD,CACrD,yCAA0C,CAC1C,8DAA+D,CAC/D,WAAc,CACd,sGAEE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,8DAA+D,CAC/D,sOAAyO,CAC3O,qEACE,mBAAsB,CACtB,uGACE,gGAAmG,CACnG,qHACE,QAAW,CACjB,yCACE,SAAY,CACZ,2CACE,iBAAkB,CAIlB,6QAA8E,CAC9E,qFAAwF,CACxF,sDACE,kIAAqI,CACvI,qDACE,mIAAsI,CAC1I,qDACE,wIAAyI,CACzI,sIAAyI,CACzI,mFACE,+FAAkG,CAClG,0FACE,aAAgB,CAClB,8FACE,+EAAkF,CACxF,sEACE,YAAa,CACb,kBAAmB,CACnB,cAAiB,CACjB,4EACE,mGAAsG,CAC1G,uEACE,gDAAiD,CACjD,QAAW,CACb,qEACE,iFAAoF,CACxF,oIAEE,iHAAoH,CACtH,kMAEE,kHAAmH,CACnH,mFAAsF,CACxF,oIAEE,iHAAkH,CAClH,kFAAqF,CACvF,sJAEE,sHAAuH,CACvH,uFAA0F,CAC5F,kCACE,oBAAqB,CACrB,kDAAqD,CACrD,oCACE,oEAAuE,CACzE,yCACE,QAAW,CACb,wNAEE,2FAA8F,CAChG,2FACE,8FAAiG,CACrG,oCACE,6EAA8E,CAC9E,iGAAkG,CAClG,mEAAsE,CACtE,2CACE,QAAW,CACb,0CACE,wGAA2G,CAC7G,2FACE,yGAA4G,CAC9G,0CACE,wGAA2G,CAC7G,mDACE,6GAAgH,CACpH,mDACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAqB,CAEzB,4BACE,2DAA4D,CAC5D,yDAA0D,CAC1D,yDAA4D,CAC5D,kEACE,8EAAiF,CAErF,6BACE,mBAAoB,CACpB,wDAAyD,CACzD,4DAA6D,CAC7D,8DAAiE,CACjE,wCACE,4CAA+C,CAEnD,qBACE,iBAAkB,CAClB,mCAAoC,CACpC,0CAA2C,CAC3C,cAAe,CACf,kDAAmD,CACnD,wDAAyD,CACzD,sDAAuD,CACvD,2BAA4B,CAC5B,gDAAmD,CACnD,sCACE,OAAU,CACZ,6CACE,iEAAkE,CAClE,mEAAsE,CAE1E,0BACE,aAAc,CACd,UAAW,CACX,6LAA8L,CAC9L,mDAAoD,CACpD,uDAAwD,CACxD,uDAAwD,CACxD,4CAA6C,CAC7C,eAAgB,CAChB,kBAAmB,CACnB,iEAAkE,CAClE,WAAc,CACd,gEACE,+EAAgF,CAChF,mGAAoG,CACpG,oBAAuB,CACzB,2EACE,kFAAmF,CACnF,sGAAuG,CACvG,mBAAsB,CACxB,oCACE,YAAa,CACb,kBAAqB,CACrB,qDACE,qBAAsB,CACtB,iBAAoB,CACtB,mEACE,YAAa,CACb,kBAAqB,CACzB,oCACE,gFAAmF,CACnF,oFACE,uDAA0D,CAEhE,+BACE,mBAAoB,CACpB,kBAAmB,CACnB,sBAAuB,CACvB,iDAAkD,CAClD,mDAAoD,CACpD,8DAAiE,CACjE,iCACE,cAAe,CACf,eAAkB,CAEtB,sCACE,+DAAgE,CAChE,wDAA2D,CAE7D,4CACE,0DAA6D,CAE/D,4BAIE,qMAA4D,CAC5D,qDAAsD,CACtD,yDAA0D,CAC1D,8CAAiD,CAEnD,kBACE,2DAA4D,CAC5D,6DAA8D,CAC9D,8DAA+D,CAC/D,4DAA6D,CAC7D,0CAA2C,CAC3C,mEAAoE,CACpE,uEAAwE,CACxE,oEAAqE,CACrE,mFAAoF,CACpF,gEAAiE,CACjE,4DAA6D,CAC7D,mEAAoE,CACpE,8EAA+E,CAC/E,qEAAsE,CACtE,iFAAoF,CACpF,sFAAyF,CACzF,+EAAgF,CAChF,qFAAwF,CACxF,qDAAsD,CACtD,uEAAwE,CACxE,yEAA0E,CAC1E,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,yEAA0E,CAC1E,sEAAuE,CACvE,yEAA0E,CAC1E,2EAA4E,CAC5E,iDAAkD,CAClD,mDAAoD,CACpD,uEAAwE,CACxE,sEAAuE,CACvE,yEAA0E,CAC1E,gDAAiD,CACjD,qFAAsF,CACtF,YAAa,CACb,kBAAmB,CACnB,sBAAuB,CACvB,6JAA8J,CAC9J,iBAAoB,CACpB,0BACE,wEAAyE,CACzE,4EAA6E,CAC7E,8EAA+E,CAC/E,0EAA2E,CAC3E,sFAAuF,CACvF,wFAAyF,CACzF,kFAAmF,CACnF,wFAAyF,CACzF,4FAA+F,CAC/F,kDACE,uDAA0D,CAC5D,uCACE,uEAA0E,CAC9E,0BACE,sFAAyF,CAC3F,0BACE,sFAAyF,CAC3F,0BACE,kFAAmF,CACnF,wFAAyF,CACzF,gFAAiF,CACjF,gHAAmH,CACnH,kDACE,uDAA0D,CAC9D,mCACE,WAAc,CAElB,2BACE,oDAAuD,CACvD,+CACE,mEAAsE,CAE1E,wBACE,yDAA0D,CAC1D,iDAAkD,CAClD,0CAA6C,CAE/C,wBACE,mDAAoD,CACpD,0CAA6C,CAE/C,gFAEE,sDAAyD,CACzD,0IAEE,iEAAoE,CAExE,6BACE,YAAa,CACb,cAAe,CACf,sBAAuB,CACvB,wDAAyD,CACzD,8DAAiE,CACjE,+BACE,mEAAoE,CACpE,qEAAsE,CACtE,iEAAoE,CAExE,qFACE,wCAA2C,CAE7C,yBACE,oFAAqF,CACrF,4EAA6E,CAC7E,uFAAwF,CACxF,gDAAiD,CACjD,sEAAuE,CACvE,oFAAqF,CACrF,qFAAsF,CACtF,oFAAqF,CACrF,yFAA0F,CAC1F,0EAA2E,CAC3E,iEAAkE,CAClE,gDAAiD,CACjD,gEAAiE,CACjE,oHAAqH,CACrH,0EAA6E,CAC7E,uCACE,kGAAmG,CACnG,8GAAiH,CAErH,iCACE,YAAa,CACb,yNAA0N,CAC1N,mDAAoD,CACpD,WAAc,CACd,uCACE,6FAAgG,CAClG,qFACE,8FAAiG,CACnG,uCACE,6FAAgG,CAEpG,sCACE,wDAAyD,CACzD,kEAAmE,CACnE,qEAAwE,CAE1E,sCACE,mEAAsE,CAExE,kCACE,6DAAgE,CAElE,qDACE,mDAAsD,CAExD,kBACE,2GAA4G,CAC5G,2FAA4F,CAC5F,4FAA6F,CAC7F,6FAA8F,CAC9F,uFAAwF,CACxF,0FAA2F,CAC3F,6EAA8E,CAC9E,6FAA8F,CAC9F,mDAAoD,CACpD,iGAAoG,CACpG,yGAA4G,CAC5G,iBAAkB,CAClB,YAAa,CACb,qBAAwB,CACxB,yCACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,6DAA8D,CAC9D,UAAW,CACX,kIAAqI,CACvI,wCACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,8EAA+E,CAC/E,6DAAgE,CAClE,+DACE,iBAAoB,CACpB,sEACE,iBAAkB,CAClB,KAAM,CACN,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,UAAW,CACX,0FAA6F,CAEnG,yDACE,uFAA0F,CAE5F,gCACE,iBAAkB,CAClB,YAAe,CACf,mDACE,aAAc,CACd,2EAA4E,CAC5E,YAAe,CAEnB,wCACE,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,8BAAkC,CAEpC,WACE,iDAAkD,CAClD,oEAAqE,CACrE,oEAAqE,CACrE,sFAAuF,CACvF,iEAAkE,CAClE,6CAA8C,CAC9C,mFAAoF,CACpF,oEAAqE,CACrE,2DAA4D,CAC5D,+DAAgE,CAChE,2EAA4E,CAC5E,sEAAuE,CACvE,oEAAqE,CACrE,oEAAqE,CACrE,sEAAuE,CACvE,sEAAuE,CACvE,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,gGAAmG,CACnG,oGAAuG,CACvG,sGAAyG,CACzG,iIAAoI,CACpI,sEAAuE,CACvE,kDAAmD,CACnD,qFAAsF,CACtF,kFAAmF,CACnF,mEAAoE,CACpE,qEAAsE,CACtE,sEAAuE,CACvE,oEAAqE,CACrE,oFAAuF,CACvF,wFAA2F,CAC3F,0FAA6F,CAC7F,sFAAyF,CACzF,gEAAiE,CACjE,iEAAkE,CAClE,4DAA6D,CAC7D,sEAAuE,CACvE,uEAAwE,CACxE,+EAAgF,CAChF,+EAAgF,CAChF,4EAA6E,CAC7E,4DAA6D,CAC7D,sDAAuD,CACvD,6EAA8E,CAC9E,yFAA0F,CAC1F,2EAA4E,CAC5E,4FAA6F,CAC7F,8EAA+E,CAC/E,mFAAsF,CACtF,+KAAkL,CAClL,wEAAyE,CACzE,0EAA2E,CAC3E,kGAAmG,CACnG,2FAA4F,CAC5F,4JAA6J,CAC7J,iGAAoG,CACpG,oGAAuG,CACvG,6EAA8E,CAC9E,6EAA8E,CAC9E,8CAA+C,CAC/C,+DAAgE,CAChE,wEAAyE,CACzE,2EAA4E,CAC5E,+CAAkD,CAClD,kGAAmG,CACnG,qGAAsG,CACtG,mEAAsE,CACtE,4JAA6J,CAC7J,mFAAoF,CACpF,+EAAgF,CAChF,kGAAqG,CACrG,qGAAwG,CACxG,gFAAiF,CACjF,sEAAuE,CACvE,yEAA0E,CAC1E,4DAA6D,CAC7D,6CAAgD,CAChD,uEAA0E,CAC1E,2FAA8F,CAC9F,2HAA8H,CAC9H,yHAA4H,CAC5H,+HAAkI,CAClI,YAAa,CACb,kCAAqC,CACrC,2BACE,yCAA4C,CAC5C,8DACE,gBAAmB,CACrB,yBACE,6CACE,YAAa,CACb,8EAA+E,CAC/E,yJAA4J,CAC9J,mDACE,uEAA0E,CAC1E,uEACE,wDAA2D,CAC/D,qDACE,aAAgB,CAAE,CACxB,4BACE,mDAAsD,CAE1D,8BACE,uDAAwD,CACxD,eAAkB,CAEpB,oBACE,YAAa,CACb,kCAAqC,CACrC,8FACE,+CAAkD,CAEtD,wBACE,qCAAsC,CACtC,2DAA8D,CAEhE,kBACE,2CAA4C,CAC5C,+CAAkD,CAClD,6BACE,qBAAwB,CAC1B,4CACE,cAAiB,CACnB,gCACE,gDAAmD,CACrD,sCACE,kBAAqB,CAEzB,uBACE,oDAAuD,CAEzD,2BACE,wDAAyD,CACzD,oDAAqD,CACrD,6CAAgD,CAElD,6BAIE,yMAA6D,CAI7D,oMAA2D,CAC3D,sDAAuD,CACvD,aAAc,CACd,QAAS,CACT,oEAAuE,CAEzE,sCACE,YAAa,CACb,kBAAqB,CACrB,wCACE,0EAA6E,CAEjF,8DACE,qCAAsC,CACtC,wEAA2E,CAE7E,wBACE,mDAAoD,CACpD,iDAAkD,CAClD,0CAA6C,CAC7C,mCACE,6EAAgF,CAClF,qCACE,+EAAkF,CACpF,qCACE,+EAAkF,CACpF,sCACE,YAAa,CACb,iBAAoB,CACtB,oCACE,iBAAkB,CAClB,SAAY,CAEhB,6BACE,4DAA6D,CAC7D,sDAAyD,CAE3D,qBACE,QAAW,CAEb,oBACE,YAAa,CACb,cAAe,CAIf,gKAAoD,CACpD,sBAIE,4LAA2D,CAE/D,wBAEE,yFAA0F,CAC1F,YAAa,CACb,wGAA0G,CAC1G,4GAA6G,CAC7G,qHAAwH,CACxH,mCACE,6CAAgD,CAClD,oFACE,0CAA6C,CAC/C,gDACE,gEAAmE,CACrE,gDACE,kHAAmH,CACnH,sHAAuH,CACvH,sHAAuH,CACvH,4HAA6H,CAC7H,2CAA8C,CAC9C,4GACE,sIAAyI,CAC7I,qEACE,2GAA8G,CAElH,+BACE,eAAkB,CAClB,YAAe,CACf,4DAA6D,CAC7D,gEAAmE,CACnE,8DACE,gHAAmH,CAEvH,sCACE,iEAAkE,CAClE,uEAA0E,CAE5E,oCACE,oBAAqB,CACrB,6DAA8D,CAC9D,iBAAkB,CAClB,gEAAiE,CACjE,mEAAsE,CAExE,+BACE,4DAA6D,CAC7D,YAAe,CACf,YAAa,CACb,sBAAuB,CACvB,4DAA6D,CAC7D,kEAAqE,CAEvE,oCACE,YAAa,CACb,qBAAsB,CACtB,WAAc,CAEhB,qCACE,YAAe,CAEjB,0CACE,WAAc,CAEhB,2CACE,sEAAuE,CACvE,6DAAgE,CAElE,uCACE,kEAAmE,CACnE,wEAAyE,CACzE,oEAAqE,CACrE,kBAAqB,CAEvB,6BACE,0DAA2D,CAC3D,YAAa,CACb,2CAA4C,CAC5C,0DAA2D,CAC3D,gEAAmE,CACnE,iEACE,6CAA8C,CAC9C,6CAAgD,CAClD,gEACE,uFAA0F,CAE9F,mBACE,4DAA6D,CAC7D,gEAAiE,CACjE,kEAAmE,CACnE,sEAAuE,CACvE,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,mCAAoC,CACpC,2EAA4E,CAC5E,oOAAyO,CACzO,6DAA8D,CAC9D,gHAAiH,CACjH,mHAAoH,CACpH,uEAAwE,CACxE,sEAAuE,CACvE,kFAAmF,CACnF,+EAAgF,CAChF,2IAA4I,CAC5I,kFAAmF,CACnF,oFAAqF,CACrF,gJAAiJ,CACjJ,uFAAwF,CACxF,yEAA0E,CAC1E,0EAA2E,CAC3E,oFAAqF,CACrF,sDAAuD,CACvD,oFAAqF,CACrF,0FAA2F,CAC3F,oIAAqI,CACrI,yFAA0F,CAC1F,0FAA2F,CAC3F,iFAAkF,CAClF,+IAAgJ,CAChJ,oFAAqF,CACrF,uEAAwE,CACxE,oGAAqG,CACrG,wDAAyD,CACzD,8JAA+J,CAC/J,gFAAiF,CACjF,gFAAiF,CACjF,kJAAmJ,CACnJ,oiBAAqiB,CACriB,mFAAoF,CACpF,mJAAoJ,CACpJ,sFAAuF,CACvF,yEAA0E,CAC1E,kHAAyH,CACzH,0DAA2D,CAC3D,oKAAqK,CACrK,uDAAwD,CACxD,kFAAmF,CACnF,wJAAyJ,CACzJ,koBAAmoB,CACnoB,iFAAkF,CAClF,+IAAgJ,CAChJ,mFAAoF,CACpF,uEAAwE,CACxE,oGAAqG,CACrG,wDAAyD,CACzD,8JAA+J,CAC/J,gFAAiF,CACjF,gFAAiF,CACjF,kJAAmJ,CACnJ,8jBAA+jB,CAC/jB,wNAA2N,CAC3N,6IAA8I,CAC9I,uEAAwE,CACxE,wFAAyF,CACzF,mHAAoH,CACpH,0hBAA2hB,CAC3hB,0LAA2L,CAC3L,+CAAgD,CAChD,mGAAoG,CACpG,uDAAwD,CACxD,+EAAgF,CAChF,+EAAgF,CAChF,sEAAuE,CACvE,sSAAuS,CACvS,wNAAyN,CACzN,oJAAsJ,CACtJ,gOAAkO,CAClO,0MAA4M,CAC5M,oJAAsJ,CACtJ,gOAAkO,CAClO,0MAA4M,CAC5M,wJAA0J,CAC1J,oOAAsO,CACtO,8MAAgN,CAChN,2/BAA4/B,CAC5/B,ogBAAqgB,CACrgB,sEAAuE,CACvE,2TAA4T,CAC5T,kDAAmD,CACnD,gGAAiG,CACjG,uDAAwD,CACxD,2JAA4J,CAC5J,gFAAiF,CACjF,mJAAoJ,CACpJ,kFAAmF,CACnF,iKAAkK,CAClK,gFAAiF,CACjF,mJAAoJ,CACpJ,iGAAkG,CAClG,mGAAoG,CACpG,iGAAkG,CAClG,kCAAmC,CACnC,UAAW,CACX,iKAAkK,CAClK,4CAA6C,CAC7C,gDAAiD,CACjD,0DAA2D,CAC3D,2BAA4B,CAC5B,kDAAmD,CACnD,sLAAuL,CACvL,oDAAqD,CACrD,oBAAqB,CACrB,uBAA0B,CAC1B,gCACE,kDAAqD,CACvD,iCACE,uCAAwC,CACxC,sBAAyB,CAC3B,6BACE,oEAAuE,CACvE,+EACE,mGAAsG,CACxG,+EACE,kGAAmG,CACnG,0GAA2G,CAC3G,0GAA6G,CACjH,yBACE,yFAA4F,CAC9F,yBACE,yFAA0F,CAC1F,6DAA8D,CAC9D,sEAAyE,CAC3E,iCACE,8FAA+F,CAC/F,kEAAmE,CACnE,2EAA8E,CAChF,4BACE,oEAAqE,CACrE,wFAAyF,CACzF,kBAAmB,CACnB,4DAA+D,CACjE,sCACE,iFAAkF,CAClF,2FAA4F,CAC5F,+DAAgE,CAChE,iEAAkE,CAClE,yEAA0E,CAC1E,iEAAkE,CAClE,wEAA2E,CAC3E,gDACE,sFAAuF,CACvF,yEAA0E,CAC1E,iFAAkF,CAClF,yEAA4E,CAChF,gCACE,iFAAkF,CAClF,2FAA4F,CAC5F,+DAAgE,CAChE,iEAAkE,CAClE,yEAA0E,CAC1E,iEAAkE,CAClE,wEAA2E,CAC3E,0CACE,sFAAuF,CACvF,yEAA0E,CAC1E,iFAAkF,CAClF,yEAA4E,CAChF,gCACE,mFAAoF,CACpF,6FAA8F,CAC9F,iEAAkE,CAClE,mEAAoE,CACpE,2EAA4E,CAC5E,mEAAoE,CACpE,0EAA6E,CAC7E,0CACE,sFAAuF,CACvF,2EAA4E,CAC5E,mFAAoF,CACpF,2EAA8E,CAClF,+BACE,gFAAiF,CACjF,kEAAmE,CACnE,0EAA2E,CAC3E,kEAAqE,CACvE,6BACE,gFAAiF,CACjF,gEAAiE,CACjE,+HAAgI,CAChI,mHAAsH,CACtH,2CACE,8FAAiG,CACnG,wCACE,2FAA8F,CAClG,yBACE,gFAAiF,CACjF,gEAAiE,CACjE,wEAAyE,CACzE,gEAAmE,CACnE,4CACE,yFAA0F,CAC1F,8GAA+G,CAC/G,iHAAmH,CACnH,8HAAgI,CAChI,kHAAsH,CACxH,sCACE,yFAA0F,CAC1F,8GAA+G,CAC/G,iHAAmH,CACnH,8HAAgI,CAChI,kHAAsH,CACxH,sCACE,2FAA4F,CAC5F,mHAAqH,CACrH,wIAA0I,CAC1I,oHAAwH,CAC5H,2BACE,kHAAmH,CACnH,kHAAmH,CACnH,sHAAyH,CAC3H,wCACE,eAAkB,CACpB,0CACE,iBAAoB,CAExB,WACE,oDAAqD,CACrD,oDAAqD,CACrD,sDAAuD,CACvD,uDAAwD,CACxD,qDAAsD,CACtD,+DAAgE,CAChE,4DAA6D,CAC7D,0DAA2D,CAC3D,sDAAuD,CACvD,+CAAgD,CAChD,2DAA4D,CAC5D,0DAA2D,CAC3D,oEAAqE,CACrE,8DAA+D,CAC/D,2FAA8F,CAC9F,YAAa,CACb,8BAA+B,CAC/B,yCAA0C,CAC1C,iIAAkI,CAClI,6BAA8B,CAC9B,kDAAmD,CACnD,wEAAyE,CACzE,sCAAyC,CACzC,8CACE,eAAgB,CAChB,kBAAqB,CAEzB,oBACE,mBAAoB,CACpB,qBAAsB,CACtB,iDAAkD,CAClD,gBAAiB,CACjB,aAAc,CACd,UAAa,CACb,qEACE,2DAA8D,CAChE,qCACE,aAAgB,CAEpB,kBACE,2CAA8C,CAEhD,iBACE,gBAAmB,CACnB,0CAA6C,CAE/C,mBACE,gBAAqB,CACrB,qCACE,yDAA4D,CAEhE,kBACE,yEAA0E,CAC1E,2FAA4F,CAC5F,yGAA0G,CAC1G,6DAA8D,CAC9D,2EAA4E,CAC5E,qFAAwF,CAE1F,yBACE,YAAa,CACb,oBAAuB,CACvB,2BACE,8DAAiE,CACnE,yCACE,6GAAgH,CAClH,qCACE,8DAAiE,CACnE,qCAEE,qBAAwB,CAC1B,0EAFE,8CAGiD,CAErD,yBACE,MAAS,CAEX,6DACE,gGAAmG,CACnG,mEACE,uGAA0G,CAE9G,8FAGE,YAAa,CACb,iBAAoB,CAEtB,+CACE,oBAAqB,CACrB,kBAAqB,CAEvB,yaAME,kBAAqB,CAEvB,iIAEE,aAAgB,CAElB,mIAEE,oBAAuB,CAEzB,qKAEE,mBAAsB,CAExB,8SAIE,YAAa,CACb,iBAAoB,CAEtB,4EACE,kDAAqD,CACrD,wDAA6D,CAE/D,mCACE,8DAAiE,CAEnE,kBACE,0EAA2E,CAC3E,iEAAkE,CAClE,mEAAoE,CACpE,kEAAmE,CACnE,iEAAkE,CAClE,uEAAwE,CACxE,2EAA4E,CAC5E,6EAA8E,CAC9E,8EAA+E,CAC/E,4EAA6E,CAC7E,gFAAiF,CACjF,oEAAqE,CACrE,gFAAiF,CACjF,iDAAkD,CAClD,kCAAmC,CACnC,YAAa,CACb,UAAW,CACX,yDAA4D,CAC5D,sBACE,gBAAmB,CACrB,yEACE,iEAAoE,CACtE,0FAEE,MAAO,CACP,WAAc,CAChB,2BACE,uDAA0D,CAE9D,wBACE,YAAa,CACb,kBAAmB,CACnB,yDAA0D,CAC1D,uDAAwD,CACxD,iDAAkD,CAClD,0CAA2C,CAC3C,iBAAkB,CAClB,+DAAgE,CAChE,uDAAwD,CACxD,0MAA6M,CAC7M,6BACE,cAAiB,CACnB,mCACE,uCAAwC,CACxC,aAAgB,CAEpB,iBACE,qCAAsC,CACtC,kEAAmE,CACnE,wCAAyC,CACzC,iEAAkE,CAClE,4EAA6E,CAC7E,mDAAoD,CACpD,+EAAgF,CAChF,kDAAmD,CACnD,0CAA2C,CAC3C,yDAA0D,CAC1D,+EAAgF,CAChF,iFAAkF,CAClF,mDAAoD,CACpD,oDAAqD,CACrD,kDAAmD,CACnD,8FAA+F,CAC/F,6DAA8D,CAC9D,8EAAiF,CACjF,gEAAiE,CACjE,kEAAmE,CACnE,mEAAoE,CACpE,iEAAkE,CAClE,4EAA6E,CAC7E,6EAA8E,CAC9E,+EAAgF,CAChF,4EAA+E,CAC/E,iDAAkD,CAClD,mDAAoD,CACpD,oDAAqD,CACrD,kDAAmD,CACnD,wDAAyD,CACzD,wFAAyF,CACzF,yDAA0D,CAC1D,wFAAyF,CACzF,kGAAmG,CACnG,mEAAoE,CACpE,kGAAmG,CACnG,oEAAqE,CACrE,qGAAsG,CACtG,8EAA+E,CAC/E,+GAAgH,CAChH,gEAAiE,CACjE,6EAA8E,CAC9E,6EAA8E,CAC9E,iFAAkF,CAClF,mEAAoE,CACpE,YAAe,CACf,6BACE,sBAAyB,CACzB,oDACE,kBAAqB,CACzB,+BACE,wFAAyF,CACzF,4FAA6F,CAC7F,8FAA+F,CAC/F,0FAA2F,CAC3F,gHAAiH,CACjH,kHAAmH,CACnH,8HAA+H,CAC/H,gIAAiI,CACjI,kJAAmJ,CACnJ,oJAAqJ,CACrJ,8FAA+F,CAC/F,qBAAwB,CAE5B,uBACE,iBAAkB,CAClB,YAAa,CACb,0DAA2D,CAI3D,iLAAyD,CACzD,8BACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,mBAAoB,CACpB,UAAW,CACX,8DAA+D,CAC/D,sOAAyO,CAC3O,8CACE,qCAAsC,CACtC,wCAAyC,CACzC,wFAAyF,CACzF,8FAA+F,CAC/F,0FAA2F,CAC3F,wDAA2D,CAE/D,uBACE,iBAAkB,CAClB,YAAa,CACb,MAAO,CAIP,iLAAuD,CACvD,oBAAqB,CACrB,0DAA6D,CAC7D,6BACE,yFAA4F,CAC9F,6BACE,yFAA0F,CAC1F,2GAA4G,CAC5G,6GAA8G,CAC9G,qGAAwG,CAC1G,8BACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,mBAAoB,CACpB,UAAW,CAGX,yHAAuO,CAAvO,6HAAuO,CAAvO,2HAAuO,CAAvO,uHAAyO,CAE7O,uBACE,wDAA2D,CAC3D,2DACE,qHAAsH,CACtH,uHAAwH,CACxH,+GAAgH,CAChH,6FAAgG,CAEpG,4BACE,8CAAiD,CAEnD,wBACE,yDAA4D,CAE9D,uBACE,YAAa,CACb,qBAAwB,CAE1B,YACE,qDAAsD,CACtD,uDAAwD,CACxD,wDAAyD,CACzD,sDAAuD,CACvD,6DAA8D,CAC9D,oEAAqE,CACrE,gDAAiD,CACjD,qDAAsD,CACtD,4CAA6C,CAC7C,sDAAuD,CACvD,+EAAgF,CAChF,uFAAwF,CACxF,wFAAyF,CACzF,yFAA0F,CAC1F,yFAA0F,CAC1F,0FAA2F,CAC3F,0FAA2F,CAC3F,oGAAqG,CACrG,oGAAqG,CACrG,qGAAsG,CACtG,qGAAsG,CACtG,wEAAyE,CACzE,sEAAuE,CACvE,sEAAuE,CACvE,oGAAqG,CACrG,oGAAqG,CACrG,iGAAkG,CAClG,8GAA+G,CAC/G,8GAA+G,CAC/G,0EAA2E,CAC3E,0EAA2E,CAC3E,uEAAwE,CACxE,qGAAsG,CACtG,qGAAsG,CACtG,mGAAoG,CACpG,gHAAiH,CACjH,gHAAiH,CACjH,0EAA2E,CAC3E,0EAA2E,CAC3E,yEAA0E,CAC1E,uGAAwG,CACxG,uGAAwG,CACxG,mGAAoG,CACpG,gHAAiH,CACjH,gHAAiH,CACjH,sEAAuE,CACvE,sEAAuE,CACvE,oEAAqE,CACrE,kGAAmG,CACnG,kGAAmG,CACnG,gGAAiG,CACjG,6GAA8G,CAC9G,6GAA8G,CAC9G,4EAA6E,CAC7E,4EAA6E,CAC7E,yEAA0E,CAC1E,uGAAwG,CACxG,uGAAwG,CACxG,qGAAsG,CACtG,kHAAmH,CACnH,kHAAmH,CACnH,wEAAyE,CACzE,yEAA0E,CAC1E,sEAAuE,CACvE,oGAAqG,CACrG,oGAAqG,CACrG,iGAAkG,CAClG,8GAA+G,CAC/G,8GAA+G,CAC/G,sEAAuE,CACvE,yFAA0F,CAC1F,wFAAyF,CACzF,yFAA0F,CAC1F,qGAAsG,CACtG,sGAAuG,CACvG,qGAAsG,CACtG,sGAAuG,CACvG,yDAA0D,CAC1D,iCAAkC,CAClC,sDAAuD,CACvD,4DAA6D,CAC7D,+DAAgE,CAChE,iFAAoF,CACpF,mFAAsF,CACtF,oFAAuF,CACvF,+DAAgE,CAChE,+DAAgE,CAChE,iEAAkE,CAClE,kEAAmE,CACnE,gEAAiE,CACjE,iBAAkB,CAClB,qIAAsI,CACtI,qCAAsC,CACtC,8BAA+B,CAC/B,kBAAmB,CACnB,mDAAoD,CACpD,QAAS,CACT,6CAAgD,CAChD,sBACE,wEAAyE,CACzE,sEAAuE,CACvE,gEAAiE,CACjE,wHAAyH,CACzH,4HAA6H,CAC7H,4HAA6H,CAC7H,kJAAmJ,CACnJ,kJAAqJ,CACvJ,uBACE,yEAA0E,CAC1E,uEAAwE,CACxE,iEAAkE,CAClE,yHAA0H,CAC1H,6HAA8H,CAC9H,6HAA8H,CAC9H,mJAAoJ,CACpJ,mJAAsJ,CACxJ,wBACE,0EAA2E,CAC3E,wEAAyE,CACzE,kEAAmE,CACnE,0HAA2H,CAC3H,8HAA+H,CAC/H,8HAA+H,CAC/H,oJAAqJ,CACrJ,oJAAuJ,CACzJ,qBACE,uEAAwE,CACxE,qEAAsE,CACtE,+DAAgE,CAChE,uHAAwH,CACxH,2HAA4H,CAC5H,2HAA4H,CAC5H,iJAAkJ,CAClJ,iJAAoJ,CACtJ,wBACE,0EAA2E,CAC3E,wEAAyE,CACzE,kEAAmE,CACnE,0HAA2H,CAC3H,8HAA+H,CAC/H,8HAA+H,CAC/H,oJAAqJ,CACrJ,oJAAuJ,CACzJ,sBACE,wEAAyE,CACzE,sEAAuE,CACvE,gEAAiE,CACjE,wHAAyH,CACzH,4HAA6H,CAC7H,4HAA6H,CAC7H,kJAAmJ,CACnJ,kJAAqJ,CACvJ,yBACE,qGAAsG,CACtG,qGAAsG,CACtG,2EAA8E,CAC9E,+IAEE,kHAAmH,CACnH,kHAAqH,CACvH,+IAEE,kHAAmH,CACnH,kHAAqH,CACzH,yBACE,6DAA8D,CAC9D,iEAAkE,CAClE,qEAAsE,CACtE,uEAAwE,CACxE,mEAAoE,CAIpE,wKAAsD,CACxD,0BACE,0EAA2E,CAC3E,qFAAsF,CACtF,sGAAuG,CACvG,sGAAuG,CACvG,gIAAiI,CACjI,gIAAiI,CACjI,gIAAiI,CACjI,gIAAmI,CAEvI,iCAEE,mBAAoB,CACpB,kBAAqB,CAEvB,kBACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,2CAA8C,CAEhD,qBACE,uCAAwC,CACxC,QAAW,CACX,4BACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,4GAA6G,CAC7G,6CAAgD,CAClD,iDAEE,cAAe,CACf,WAAc,CACd,2KAIE,oBAAuB,CACzB,6DAEE,uGAAwG,CACxG,uGAA0G,CAC5G,6DAEE,uGAAwG,CACxG,uGAA0G,CAEhH,kBACE,iDAAkD,CAClD,oCAAuC,CAEzC,kBACE,4EAA+E,CAC/E,2EAA8E,CAC9E,uEAAwE,CACxE,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,qFAAsF,CACtF,+EAAgF,CAChF,6EAA8E,CAC9E,8EAA+E,CAC/E,sFAAuF,CACvF,mEAAoE,CACpE,yCAA0C,CAC1C,gFAAiF,CACjF,kEAAmE,CACnE,wCAAyC,CACzC,0EAA6E,CAC7E,6EAAgF,CAChF,gGAAmG,CACnG,kGAAqG,CACrG,8EAA+E,CAC/E,0FAA2F,CAC3F,yFAA0F,CAC1F,uEAAwE,CACxE,wEAAyE,CACzE,mBAAsB,CACtB,gCAIE,6MAA8D,CAC9D,qEAAsE,CACtE,8GAA+G,CAC/G,+DAAkE,CACpE,gCACE,uCAAwC,CACxC,wCAAyC,CACzC,4CAA6C,CAC7C,wCAAyC,CACzC,gGAAiG,CACjG,0FAA2F,CAC3F,4FAA6F,CAC7F,yCAA0C,CAC1C,8FAA+F,CAC/F,0GAA6G,CAC7G,iDACE,sBAAyB,CAC3B,wDACE,qBAAsB,CACtB,sBAAyB,CAC3B,wDACE,qBAAwB,CAC1B,wEACE,6CAAgD,CAClD,sEACE,4FAA6F,CAC7F,8FAAiG,CAEvG,wBACE,YAAa,CACb,MAAO,CACP,cAAe,CACf,oBAAuB,CAEzB,wBACE,mBAAoB,CACpB,cAAe,CACf,uDAAwD,CACxD,yDAA4D,CAE9D,6BACE,mBAAoB,CACpB,4DAA6D,CAC7D,8DAAiE,CAEnE,yBACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,kDAAmD,CACnD,wDAAyD,CACzD,0DAA2D,CAC3D,kDAAqD,CAEvD,yBAIE,oLAAyD,CAE3D,WACE,qDAAsD,CACtD,2DAA4D,CAC5D,4DAA6D,CAC7D,sDAAuD,CACvD,uDAAwD,CACxD,mEAAoE,CACpE,0CAA6C,CAC7C,4BAEE,8CAA+C,CAC/C,gDAAmD,CACrD,iBACE,0CAA6C,CAC/C,+BACE,0CAA6C,CAC/C,uBACE,0BAA2B,CAC3B,YAAa,CACb,cAAiB,CACjB,0BACE,4BAA+B,CAC/B,2CACE,wDAA2D,CAEnE,YACE,qDAAsD,CACtD,wDAAyD,CACzD,sCAAuC,CACvC,wEAAyE,CACzE,0CAA2C,CAC3C,0CAA2C,CAC3C,6CAA8C,CAC9C,8CAA+C,CAC/C,yEAA2E,CAC3E,+DAAgE,CAChE,8DAA+D,CAC/D,+DAAgE,CAChE,oEAAqE,CACrE,iEAAkE,CAClE,wEAAyE,CACzE,6EAA8E,CAC9E,gFAAiF,CACjF,6DAA8D,CAC9D,mEAAoE,CACpE,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,yEAA0E,CAC1E,wEAAyE,CACzE,iEAAkE,CAClE,8DAA+D,CAC/D,yEAA0E,CAC1E,kDAAmD,CACnD,uEAAwE,CACxE,kEAAmE,CACnE,mEAAoE,CACpE,iEAAkE,CAClE,uEAAwE,CACxE,sEAAuE,CACvE,sEAAuE,CACvE,6EAA8E,CAC9E,wEAAyE,CACzE,2EAA4E,CAC5E,2EAA4E,CAC5E,0EAA2E,CAC3E,+EAAgF,CAChF,8EAA+E,CAC/E,+EAAgF,CAChF,wFAAyF,CACzF,yFAA0F,CAC1F,0FAA2F,CAC3F,8FAA+F,CAC/F,uEAAwE,CACxE,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,sFAAuF,CACvF,4EAA6E,CAC7E,8DAA+D,CAC/D,+DAAgE,CAChE,qEAAsE,CACtE,0EAA2E,CAC3E,YAAa,CACb,sBAAuB,CACvB,gBAAiB,CACjB,yCAA0C,CAC1C,+CAAkD,CAClD,0BACE,YACE,4EAA+E,CAAE,CACrF,yBACE,YACE,oCAAqC,CACrC,mCAAsC,CAAE,CAC5C,0BACE,YACE,8EAA+E,CAC/E,gGAAiG,CAGjG,kCAHmG,CAAE,CAIzG,yBACE,YACE,wFAAyF,CACzF,sFAAuF,CACvF,kGAAmG,CAGnG,oFAAqF,CACrF,kFAJqG,CAAE,CAK3G,yBACE,YACE,oCAAqC,CACrC,mCAAsC,CAAE,CAC5C,0BACE,YACE,0FAA2F,CAG3F,uDAH6F,CAAE,CAInG,yBACE,YACE,kBAAqB,CAAE,CAE7B,uBACE,UAAW,CACX,gDAAmD,CACnD,0BACE,uBACE,YAAa,CACb,sBAAuB,CACvB,+DAAgE,CAChE,2EAA4E,CAC5E,wDAAyD,CACzD,wDAAyD,CACzD,sDAAyD,CAAE,CAEjE,oBACE,kCAAmC,CACnC,gBAAiB,CACjB,qDAAsD,CACtD,mDAAsD,CACtD,0BACE,oBACE,mDAAsD,CAAE,CAC5D,gCACE,8DAAiE,CAErE,kBACE,mDAAoD,CACpD,yDAA0D,CAC1D,cAAiB,CACjB,6DACE,sDAAyD,CAC3D,4DACE,4DAA+D,CAEnE,yBACE,YAAa,CACb,0BAA2B,CAC3B,oDAAqD,CACrD,8CAA+C,CAC/C,kBAAmB,CACnB,yLAA4L,CAC5L,yBACE,yBACE,8BAAiC,CAAE,CACvC,wCACE,gBAAiB,CACjB,aAAgB,CAChB,yBACE,wCACE,eAAkB,CAClB,UAAa,CAAE,CAEvB,8BACE,+DAAgE,CAChE,uDAAwD,CACxD,gBAAqB,CAEvB,uBACE,wDAAyD,CACzD,0DAA2D,CAC3D,sDAAyD,CAE3D,yBACE,YAAa,CACb,cAAiB,CACjB,qCACE,mEAAoE,CACpE,iBAAoB,CACtB,2BACE,eAAkB,CAEtB,+BACE,YAAa,CACb,cAAe,CACf,sBAAuB,CACvB,iNAAoN,CAEtN,oCACE,qEAAsE,CACtE,mEAAoE,CACpE,qEAAwE,CAE1E,6CACE,6DAA8D,CAC9D,UAAW,CACX,mEAAoE,CACpE,WAAY,CACZ,qEAAwE,CAE1E,mDACE,oEAAuE,CAEzE,8BACE,6MAA8M,CAC9M,iBAAkB,CAClB,qEAAwE,CACxE,kCACE,gEAAmE,CAEvE,oBACE,kCAAmC,CACnC,gBAAiB,CACjB,qDAAsD,CACtD,mDAAsD,CACtD,iCACE,WAAc,CAChB,gDACE,yDAA4D,CAEhE,WACE,kCAAmC,CACnC,yEAA0E,CAC1E,sDAAuD,CACvD,oDAAqD,CACrD,uDAAwD,CACxD,sEAAyE,CACzE,8DAA+D,CAC/D,iEAAkE,CAClE,4DAA6D,CAC7D,8DAA+D,CAC/D,+DAAgE,CAChE,6DAA8D,CAC9D,0DAA2D,CAC3D,iEAAkE,CAClE,mDAAoD,CACpD,2FAA4F,CAC5F,0DAA2D,CAC3D,4DAA6D,CAC7D,6DAA8D,CAC9D,2DAA4D,CAC5D,0CAAqD,CACrD,0DAA2D,CAC3D,kEAAmE,CACnE,8DAA+D,CAC/D,oEAAqE,CACrE,uEAAwE,CACxE,2EAA4E,CAC5E,6EAA8E,CAC9E,yEAA0E,CAC1E,iEAAkE,CAClE,4EAA6E,CAC7E,iEAAkE,CAClE,sEAAuE,CACvE,iEAAkE,CAClE,gEAAiE,CACjE,wEAAyE,CACzE,uEAAwE,CACxE,iFAAkF,CAClF,iFAAkF,CAClF,sEAAuE,CACvE,wEAAyE,CACzE,4EAA6E,CAC7E,8EAA+E,CAC/E,0EAA2E,CAC3E,oFAAqF,CACrF,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,sEAAuE,CACvE,+GAAkH,CAClH,8EAA+E,CAC/E,uFAAwF,CACxF,yFAA0F,CAC1F,kGAAmG,CACnG,oCAAqC,CACrC,qCAAsC,CACtC,4FAA6F,CAC7F,yFAA0F,CAC1F,4KAA8K,CAC9K,2EAA8E,CAC9E,oGAAqG,CACrG,qGAAsG,CACtG,4MAA8M,CAC9M,kGAAmG,CACnG,uHAAwH,CACxH,0GAA2G,CAC3G,wCAAyC,CACzC,8CAA+C,CAC/C,kDAAmD,CACnD,sCAAyC,CACzC,kCACE,iBAAkB,CAClB,yCAA0C,CAC1C,SAAY,CACd,6CACE,iBAAoB,CACtB,0BACE,0CAA2C,CAC3C,4CAA6C,CAC7C,eAAgB,CAChB,oDAAuD,CACvD,0IAEE,2BAA8B,CAChC,qCACE,2BAA4B,CAC5B,iBAAkB,CAClB,8CAA+C,CAC/C,SAAU,CACV,UAAW,CACX,4DAA+D,CAC/D,qDACE,2BAA8B,CAClC,2CACE,iBAAkB,CAClB,0DAA6D,CAC/D,6EACE,yFAA4F,CAC9F,mFACE,iBAAoB,CACtB,2CACE,oDAAuD,CAC3D,yBACE,iDAAkD,CAClD,uDAA0D,CAE9D,mBAIE,iKAAqD,CAEvD,sBACE,YAAa,CACb,wCAAyC,CACzC,6DAAgE,CAChE,uGACE,uEAAwE,CACxE,2FAA8F,CAC9F,qKACE,SAAY,CAChB,qDACE,gEAAiE,CACjE,mBAAsB,CAE1B,iBACE,YAAa,CACb,eAAgB,CAChB,qBAAsB,CACtB,WAAY,CAIZ,yJAAiD,CACjD,0CAA2C,CAC3C,8CAA+C,CAC/C,8CAA+C,CAC/C,mCAAoC,CACpC,eAAgB,CAChB,wDAAyD,CACzD,WAAc,CACd,uBACE,oBAAuB,CACzB,0BACE,gEAAiE,CACjE,mBAAsB,CACxB,4DACE,SAAY,CAEhB,sBACE,YAAa,CACb,kBAAmB,CACnB,UAAa,CACb,qDACE,kEAAmE,CACnE,8DAA+D,CAC/D,uDAAwD,CACxD,SAAY,CAEhB,sBACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,WAAc,CAEhB,wBAIE,qLAAwD,CACxD,iDAAkD,CAClD,qDAAsD,CACtD,0CAA6C,CAE/C,6BACE,sDAAuD,CACvD,+CAAgD,CAChD,oBAAuB,CAEzB,sBACE,qDAAwD,CAE1D,6BACE,8DAA+D,CAC/D,4DAA+D,CAEjE,mDACE,qEAAwE,CAE1E,mDACE,qEAAwE,CAE1E,6BACE,0DAA2D,CAC3D,sDAAuD,CACvD,+CAAgD,CAChD,SAAY,CAEd,wBACE,YAAa,CAIb,qLAAwD,CACxD,WAAc,CACd,4DACE,sFAAyF,CAC3F,sCACE,2FAA8F,CAC9F,qDACE,wGAA2G,CAC7G,mEACE,mEAAsE,CAE5E,6BACE,YAAa,CACb,kBAAmB,CACnB,iDAAkD,CAClD,+CAAkD,CAEpD,gBACE,wEAAyE,CACzE,2DAA4D,CAC5D,qDAAsD,CACtD,4BAA6B,CAC7B,oEAAqE,CACrE,0CAA2C,CAC3C,qCAAsC,CACtC,0CAA2C,CAC3C,sEAAuE,CACvE,kEAAmE,CACnE,sEAAuE,CACvE,mFAAoF,CACpF,iLAAkL,CAClL,sIAAuI,CACvI,iFAAkF,CAClF,mFAAoF,CACpF,mFAAoF,CACpF,6EAA8E,CAC9E,mFAAoF,CACpF,iEAAkE,CAClE,mEAAoE,CACpE,kEAAmE,CACnE,gFAAiF,CACjF,oEAAqE,CACrE,qFAAsF,CACtF,iEAAkE,CAClE,sEAAuE,CACvE,gEAAiE,CACjE,sEAAuE,CACvE,uGAA0G,CAC1G,+DAAgE,CAChE,iEAAkE,CAClE,gEAAiE,CACjE,8EAA+E,CAC/E,uEAAwE,CACxE,4DAAmE,CACnE,8DAA+D,CAC/D,kHAAmH,CACnH,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,4EAA6E,CAC7E,gHAAmH,CACnH,iBAAkB,CAClB,qCAAsC,CACtC,YAAa,CACb,qBAAsB,CACtB,kCAAmC,CACnC,yCAA0C,CAC1C,2CAA4C,CAC5C,uDAAwD,CACxD,2CAA8C,CAC9C,0BACE,gBACE,oFAAuF,CAAE,CAC7F,wBACE,iEAAoE,CACtE,wBACE,0DAA6D,CAC/D,wBACE,iEAAoE,CACtE,+BACE,iDAAkD,CAClD,qBAAsB,CACtB,sDAAuD,CACvD,wDAA2D,CAC7D,4BACE,sFAAyF,CAC3F,6BACE,uFAA0F,CAC5F,6BACE,uFAA0F,CAC5F,6BACE,uFAA0F,CAC5F,0BACE,oFAAuF,CACzF,6BACE,iBAAkB,CAClB,wCAAyC,CACzC,4CAA+C,CAC/C,+BACE,kEAAqE,CAE3E,wBACE,YAAa,CACb,qBAAsB,CACtB,qDAAsD,CACtD,yDAA0D,CAC1D,uDAA0D,CAC1D,kCACE,YAAa,CACb,kBAAqB,CACvB,mCACE,uEAA0E,CAC5E,8CACE,kFAAqF,CAEzF,6BACE,WAAY,CACZ,WAAc,CAEhB,mDAEE,eAAgB,CAChB,sBAAuB,CACvB,kBAAqB,CAEvB,uBACE,aAAc,CACd,oDAAqD,CACrD,gDAAiD,CACjD,oDAAuD,CACvD,iCACE,YAAe,CAEnB,4BACE,2DAA4D,CAC5D,8CAAiD,CAEnD,6BACE,0DAA6D,CAE/D,sBACE,aAAc,CACd,iDAAkD,CAClD,mDAAoD,CACpD,uDAAwD,CACxD,qDAAsD,CACtD,iBAAkB,CAClB,eAAgB,CAChB,2BAA4B,CAC5B,qBAAsB,CACtB,gCAAmC,CACnC,iCACE,qEAAwE,CAE5E,wBACE,YAAa,CACb,aAAc,CACd,kBAAmB,CAInB,qLAA0D,CAC1D,sDACE,iEAAoE,CACpE,oCACE,sDACE,wGAA2G,CAAE,CAErH,UACE,mDAAoD,CACpD,uDAAwD,CACxD,iFAAkF,CAClF,yHAA0H,CAC1H,kEAAmE,CACnE,yEAA0E,CAC1E,yEAA0E,CAC1E,0EAA2E,CAC3E,6EAA8E,CAC9E,8FAA+F,CAC/F,8FAA+F,CAC/F,+FAAgG,CAChG,kGAAmG,CACnG,iFAAkF,CAClF,iFAAkF,CAClF,4FAA6F,CAC7F,2EAA4E,CAC5E,wFAAyF,CACzF,kFAAmF,CACnF,oGAAqG,CACrG,oGAAqG,CACrG,qGAAsG,CACtG,oGAAqG,CACrG,6BAA8B,CAC9B,+GAAgH,CAChH,wGAAyG,CACzG,iFAAkF,CAClF,uEAAwE,CACxE,yDAA0D,CAC1D,iEAAkE,CAClE,yDAA0D,CAC1D,2DAA4D,CAC5D,4DAA6D,CAC7D,0DAA2D,CAC3D,+DAAgE,CAChE,8DAA+D,CAC/D,0DAA2D,CAC3D,iEAAkE,CAClE,iEAAkE,CAClE,kEAAmE,CACnE,qEAAsE,CACtE,6CAA8C,CAC9C,oFAAqF,CACrF,oFAAqF,CACrF,qFAAsF,CACtF,wFAAyF,CACzF,qEAAwE,CACxE,iFAAkF,CAClF,6EAA8E,CAC9E,oDAAqD,CACrD,oDAAqD,CACrD,qDAAsD,CACtD,wDAAyD,CACzD,wEAAyE,CACzE,+EAAgF,CAChF,+EAAgF,CAChF,gFAAiF,CACjF,mFAAoF,CACpF,0CAA2C,CAC3C,iDAAkD,CAClD,iDAAkD,CAClD,kDAAmD,CACnD,qFAAsF,CACtF,uEAAwE,CACxE,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,2EAA4E,CAC5E,8EAA+E,CAC/E,kEAAmE,CACnE,iEAAkE,CAClE,wEAAyE,CACzE,gFAAiF,CACjF,gFAAiF,CACjF,iFAAkF,CAClF,oFAAqF,CACrF,2DAA4D,CAC5D,kEAAmE,CACnE,kEAAmE,CACnE,mEAAoE,CACpE,sEAAuE,CACvE,uFAAwF,CACxF,qDAAsD,CACtD,4FAA6F,CAC7F,4FAA6F,CAC7F,6FAA8F,CAC9F,gGAAiG,CACjG,qEAAsE,CACtE,uEAAwE,CACxE,wEAAyE,CACzE,sEAAuE,CACvE,gEAAiE,CACjE,+DAAgE,CAChE,qEAAsE,CACtE,8EAA+E,CAC/E,8EAA+E,CAC/E,+EAAgF,CAChF,kFAAmF,CACnF,yDAA0D,CAC1D,gEAAiE,CACjE,gEAAiE,CACjE,iEAAkE,CAClE,oEAAqE,CACrE,qFAAsF,CACtF,mDAAoD,CACpD,0FAA2F,CAC3F,0FAA2F,CAC3F,2FAA4F,CAC5F,8FAA+F,CAC/F,8EAA+E,CAC/E,uFAAwF,CACxF,uFAAwF,CACxF,wFAAyF,CACzF,4FAA6F,CAC7F,6FAA8F,CAC9F,0GAA2G,CAC3G,8DAA+D,CAC/D,sEAAuE,CACvE,qCAAsC,CACtC,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,iEAAkE,CAClE,sFAAuF,CACvF,sFAAuF,CACvF,uFAAwF,CACxF,2FAA4F,CAC5F,qFAAsF,CACtF,qFAAsF,CACtF,sFAAuF,CACvF,yFAA0F,CAC1F,+BAAgC,CAChC,oDAAqD,CACrD,wEAAyE,CACzE,uEAAwE,CACxE,2DAA4D,CAC5D,iEAAkE,CAClE,kEAAmE,CACnE,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,wEAAyE,CACzE,uEAAwE,CACxE,kEAAmE,CACnE,sDAAuD,CACvD,gEAAiE,CACjE,gEAAiE,CACjE,iEAAkE,CAClE,4FAA6F,CAC7F,sDAAuD,CACvD,sDAAuD,CACvD,uDAAwD,CACxD,0FAA2F,CAC3F,oEAAqE,CACrE,kEAAmE,CACnE,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,wEAAyE,CACzE,uEAAwE,CACxE,kEAAmE,CACnE,mEAAoE,CACpE,wFAAyF,CACzF,8EAA+E,CAC/E,mEAAoE,CACpE,2EAA4E,CAC5E,2EAA4E,CAC5E,4EAA6E,CAC7E,gFAAiF,CACjF,sDAAuD,CACvD,wEAAyE,CACzE,8EAAiF,CACjF,gFAAmF,CACnF,0FAA2F,CAC3F,gFAAiF,CACjF,qDAAsD,CACtD,oDAAqD,CACrD,oEAAqE,CACrE,6DAA8D,CAC9D,4DAA6D,CAC7D,iEAAkE,CAClE,wEAAyE,CACzE,6DAA8D,CAC9D,gEAAiE,CACjE,qCAAsC,CACtC,oCAAqC,CACrC,kFAAqF,CACrF,qCACE,UACE,sEAAuE,CACvE,oEAAqE,CACrE,wFAAyF,CACzF,sFAAuF,CACvF,wFAAyF,CACzF,sFAAuF,CACvF,wEAA2E,CAAE,CACjF,kDACE,eAAkB,CAClB,oIAGE,iBAAkB,CAClB,YAAe,CACjB,kFACE,MAAO,CACP,cAAe,CACf,eAAgB,CAChB,kBAAmB,CACnB,gCAAiC,CACjC,oBAAqB,CACrB,2CAA8C,CAC9C,wHACE,YAAe,CACnB,kFACE,YAAe,CACjB,kFACE,kBAAmB,CACnB,kBAAmB,CACnB,kBAAqB,CACrB,gGACE,QAAS,CACT,QAAW,CACb,8FACE,YAAe,CACrB,iDACE,gDAAiD,CACjD,8CAAiD,CACnD,+CACE,8CAA+C,CAC/C,4CAA+C,CACjD,qBACE,yFAA0F,CAC1F,qJAAsJ,CACtJ,6DAA8D,CAC9D,2EAA4E,CAC5E,2EAA4E,CAC5E,6EAA8E,CAC9E,mFAAoF,CACpF,+FAAgG,CAChG,+FAAgG,CAChG,iGAAkG,CAClG,uGAAwG,CACxG,yFAA0F,CAC1F,uFAAwF,CACxF,6GAA8G,CAC9G,qHAAsH,CACtH,qHAAsH,CACtH,uHAAwH,CACxH,6HAA8H,CAC9H,+EAAgF,CAChF,uGAA0G,CAC1G,mCACE,2FAA8F,CAClG,0BACE,4EAA6E,CAC7E,gFAAiF,CACjF,kFAAmF,CACnF,8EAA+E,CAC/E,kEAAmE,CACnE,gEAAiE,CACjE,kEAAmE,CACnE,gFAAiF,CACjF,kFAAmF,CACnF,gFAAiF,CACjF,wFAAyF,CACzF,sFAAuF,CACvF,oGAAqG,CACrG,oGAAqG,CACrG,sGAAuG,CACvG,4GAA6G,CAC7G,8FAA+F,CAC/F,oGAAqG,CACrG,kHAAmH,CACnH,kHAAmH,CACnH,oHAAqH,CACrH,0HAA6H,CAC/H,wBACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gEAAiE,CACjE,8DAA+D,CAC/D,gEAAiE,CACjE,8EAA+E,CAC/E,gFAAiF,CACjF,8EAA+E,CAC/E,sFAAuF,CACvF,oFAAqF,CACrF,kGAAmG,CACnG,kGAAmG,CACnG,oGAAqG,CACrG,0GAA2G,CAC3G,4FAA6F,CAC7F,kGAAmG,CACnG,gHAAiH,CACjH,gHAAiH,CACjH,kHAAmH,CACnH,wHAAyH,CACzH,kFAAmF,CACnF,gGAAiG,CACjG,gGAAiG,CACjG,kGAAmG,CACnG,sGAAuG,CACvG,8GAA+G,CAC/G,kIAAqI,CACvI,wBACE,kFAAmF,CACnF,sDAAuD,CACvD,oDAAqD,CACrD,gDAAiD,CACjD,sDAAyD,CAC3D,mDACE,SAAY,CACd,iEACE,cAAe,CACf,uBAA0B,CAC5B,kEACE,aAAc,CACd,uBAA0B,CAE9B,gBACE,aAAgB,CAElB,gBACE,iBAAkB,CAClB,2CAA8C,CAC9C,gCACE,6CAAgD,CAChD,uCACE,iBAAkB,CAClB,OAAQ,CACR,0DAA6D,CAC7D,MAAO,CACP,UAAW,CACX,yGAA4G,CAElH,gBACE,iBAAkB,CAClB,YAAa,CACb,oBAAqB,CACrB,qJAAsJ,CACtJ,yCAA0C,CAC1C,6CAA8C,CAC9C,kCAAmC,CACnC,uDAAwD,CACxD,mDAAsD,CACtD,6CACE,iBAAkB,CAClB,UAAW,CACX,cAAiB,CACnB,uBACE,OAAQ,CACR,gEAAmE,CACnE,MAAO,CACP,uDAAwD,CACxD,oEAAuE,CACzE,sBACE,KAAM,CACN,QAAS,CACT,MAAO,CAGP,cAAgE,CAAhE,sDAAgE,CAAhE,yGAAkE,CACpE,sBACE,yCAA0C,CAC1C,8DAAiE,CACjE,6BACE,2EAA8E,CAChF,4BACE,6DAA8D,CAC9D,sEAAyE,CAC7E,sBACE,yCAA0C,CAC1C,8DAAiE,CACjE,6BACE,2EAA8E,CAChF,4BACE,6DAA8D,CAC9D,sEAAyE,CAC7E,uBACE,0CAA2C,CAC3C,+DAAkE,CAClE,8BACE,4EAA+E,CACjF,6BACE,8DAA+D,CAC/D,uEAA0E,CAC9E,iIAEE,6CAA8C,CAC9C,kEAAqE,CACrE,sJAEE,+EAAkF,CACpF,mJAEE,iEAAkE,CAClE,0EAA6E,CACjF,mFACE,UAAW,CACX,oBAAqB,CACrB,WAAc,CAElB,kBACE,sEAAuE,CACvE,0EAA2E,CAC3E,4EAA6E,CAC7E,wEAAyE,CACzE,kEAAmE,CACnE,oGAAqG,CACrG,oGAAqG,CACrG,sGAAuG,CACvG,4GAA6G,CAC7G,wGAAyG,CACzG,wGAAyG,CACzG,0GAA2G,CAC3G,gHAAiH,CACjH,oFAAqF,CACrF,kFAAmF,CACnF,6CAA8C,CAC9C,qDAAsD,CACtD,iDAAkD,CAClD,sCAAuC,CACvC,oBAAqB,CACrB,2CAA8C,CAC9C,gDACE,kFAAmF,CACnF,eAAgB,CAChB,SAAY,CACd,qCACE,YAAe,CAEnB,kBACE,SAAU,CACV,mDAAoD,CACpD,iDAAkD,CAClD,gBAAiB,CACjB,2CAA4C,CAC5C,aAAgB,CAElB,uBACE,oBAAqB,CACrB,mDAAsD,CACtD,qDACE,wEAA2E,CAE/E,mBACE,qEAAsE,CACtE,uEAAwE,CACxE,2EAA4E,CAC5E,6EAA8E,CAC9E,yEAA0E,CAC1E,mEAAoE,CACpE,qGAAsG,CACtG,qGAAsG,CACtG,qGAAsG,CACtG,uGAAwG,CACxG,6GAA8G,CAC9G,yGAA0G,CAC1G,yGAA0G,CAC1G,2GAA4G,CAC5G,iHAAkH,CAClH,8CAA+C,CAC/C,qCAAwC,CACxC,sCACE,2EAA8E,CAElF,yBACE,yLAA0L,CAC1L,kDAAmD,CACnD,2CAA4C,CAC5C,uHAA0H,CAE5H,yBACE,SAAU,CACV,2CAA4C,CAC5C,2CAA4C,CAC5C,gEAAiE,CACjE,QAAS,CACT,4DAA6D,CAC7D,SAAU,CACV,qDAAwD,CACxD,gCACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,UAAW,CACX,gEAAiE,CACjE,yEAAkI,CAAlI,qBAAkI,CAAlI,2EAAkI,CAAlI,kBAAoI,CACtI,+BACE,kDAAqD,CACvD,+BACE,kDAAqD,CACvD,gCACE,mDAAsD,CACxD,kCACE,qDAAsD,CACtD,0EAA6E,CAC/E,uCACE,uGAAwG,CACxG,2DAA8D,CAC9D,2BAA8B,CAC9B,8CACE,OAAU,CACd,wCACE,sGAAuG,CACvG,0DAA6D,CAC7D,0BAA6B,CAC7B,+CACE,MAAS,CAEf,yBACE,4EAA6E,CAC7E,oEAAqE,CACrE,+EAAgF,CAChF,mEAAoE,CACpE,oFAAuF,CACvF,4EAA+E,CAC/E,uFAA0F,CAC1F,2EAA8E,CAC9E,yDAA0D,CAC1D,iFAAkF,CAClF,+CAAgD,CAChD,uCAAwC,CACxC,yCAA0C,CAC1C,4CAA6C,CAC7C,6CAA8C,CAC9C,6DAA8D,CAC9D,8CAA+C,CAC/C,8CAA+C,CAC/C,wCAAyC,CACzC,yCAA0C,CAC1C,iEAAkE,CAClE,qEAAsE,CACtE,6EAA8E,CAC9E,+FAAgG,CAChG,uGAAwG,CACxG,gFAAiF,CACjF,kGAAmG,CACnG,yGAA0G,CAC1G,yEAA0E,CAC1E,8FAA+F,CAC/F,iBAAkB,CAClB,oBAAqB,CACrB,yLAA0L,CAC1L,oLAAqL,CACrL,uEAAwE,CACxE,iEAAoE,CACpE,gCACE,iBAAkB,CAClB,8CAA+C,CAC/C,kDAAmD,CACnD,QAAS,CACT,MAAO,CACP,kDAAmD,CACnD,oDAAqD,CACrD,UAAW,CACX,kHAAmH,CACnH,iEAAkE,CAClE,wHAA4H,CAC9H,2BACE,8CAA+C,CAC/C,gDAAmD,CACrD,2BACE,iBAAoB,CACtB,wFAEE,oBAAqB,CACrB,8EAAiF,CACjF,sGAEE,qBAAwB,CAC5B,mCACE,gHAAiH,CACjH,wGAA2G,CAC7G,qCACE,kHAAmH,CACnH,qDAAwD,CACxD,2CACE,yHAA4H,CAChI,wCACE,qHAAsH,CACtH,wDAA2D,CAC3D,8CACE,4HAA+H,CAErI,gCACE,6DAAgE,CAElE,0BACE,kFAAmF,CACnF,2EAA4E,CAC5E,6EAA8E,CAC9E,8EAA+E,CAC/E,4EAA6E,CAC7E,0FAA2F,CAC3F,oFAAqF,CACrF,uEAAwE,CACxE,iFAAkF,CAClF,kFAAmF,CACnF,qEAAsE,CACtE,8EAA+E,CAC/E,gFAAiF,CACjF,iFAAkF,CAClF,+EAAgF,CAChF,6FAA8F,CAC9F,6FAA8F,CAC9F,0FAA2F,CAC3F,oEAAqE,CACrE,6DAA8D,CAC9D,sFAAuF,CACvF,oDAAqD,CACrD,4HAA+H,CAC/H,6GAA8G,CAC9G,mHAAoH,CACpH,mHAAoH,CACpH,yHAA0H,CAC1H,iHAAkH,CAClH,uHAAwH,CACxH,mHAAoH,CACpH,yHAA0H,CAC1H,mHAAoH,CACpH,yHAA0H,CAC1H,qGAAsG,CACtG,mGAAoG,CACpG,iIAAoI,CACpI,+DAAgE,CAChE,kFAAmF,CACnF,uEAAwE,CACxE,gHAAiH,CACjH,8FAA+F,CAC/F,+IAAiJ,CACjJ,uFAAwF,CACxF,gEAAiE,CACjE,2FAA4F,CAC5F,iGAAkG,CAClG,+DAAgE,CAChE,sHAAuH,CACvH,4FAA6F,CAC7F,mFAAoF,CACpF,wFAAyF,CACzF,sGAAuG,CACvG,uGAAwG,CACxG,0DAA2D,CAC3D,iFAAkF,CAClF,mFAAoF,CACpF,oFAAqF,CACrF,kFAAmF,CACnF,gGAAiG,CACjG,wFAAyF,CACzF,6FAA8F,CAC9F,gEAAiE,CACjE,wFAAyF,CACzF,2DAA4D,CAC5D,wFAAyF,CACzF,uFAAwF,CACxF,iFAAkF,CAClF,wEAAyE,CACzE,8EAA+E,CAC/E,YAAa,CACb,qBAAsB,CACtB,WAAY,CACZ,iEAAoE,CAEtE,kCACE,iBAAkB,CAClB,uDAAwD,CACxD,YAAa,CACb,aAAc,CACd,oBAAqB,CACrB,6NAA8N,CAC9N,yEAA0E,CAC1E,6DAAgE,CAElE,wCACE,iEAAoE,CAEtE,yCACE,sEAAyE,CAE3E,yCACE,YAAa,CACb,kBAAmB,CACnB,gBAAmB,CAErB,gCACE,eAAgB,CAChB,wDAA2D,CAE7D,qCACE,iBAAkB,CAClB,YAAa,CACb,8BAA+B,CAC/B,yOAA0O,CAC1O,4EAA6E,CAC7E,+IAAgJ,CAChJ,wEAAyE,CACzE,gEAAmE,CACnE,gGACE,qDAAwD,CAC1D,oGACE,yHAA0H,CAC1H,6HAAgI,CAClI,4CACE,iBAAkB,CAClB,2DAA4D,CAC5D,iEAAkE,CAClE,+DAAgE,CAChE,UAAW,CACX,oFAAuF,CACzF,+CACE,qJAAsJ,CACtJ,yIAA4I,CAC9I,kDACE,wJAAyJ,CACzJ,4IAA+I,CACjJ,iDACE,uJAAwJ,CACxJ,2IAA8I,CAChJ,kDACE,wJAAyJ,CACzJ,4IAA+I,CACjJ,kDACE,wJAAyJ,CACzJ,4IAA+I,CACjJ,+CACE,8HAA+H,CAC/H,0HAA2H,CAC3H,kHAAmH,CACnH,wHAAyH,CACzH,0IAA2I,CAC3I,qJAAsJ,CACtJ,iBAAoB,CACtB,oDACE,cAAiB,CACjB,0DACE,8EAA+E,CAC/E,oFAAuF,CAE7F,4CACE,YAAa,CACb,oBAAqB,CACrB,eAAkB,CAClB,YAAe,CACf,6EAAgF,CAElF,iDACE,gFAAiF,CACjF,mEAAsE,CAExE,kDACE,+EAAgF,CAChF,qBAAwB,CACxB,gEACE,mBAAoB,CACpB,2BAA4B,CAC5B,qFAAsF,CACtF,eAAkB,CAEtB,4CACE,eAAkB,CAClB,YAAiB,CAEnB,iDACE,YAAe,CACf,eAAkB,CAClB,kFAAmF,CACnF,qBAAwB,CAE1B,+CACE,YAAe,CACf,eAAkB,CAClB,wEAAyE,CACzE,iEAAoE,CAEtE,sCACE,YAAa,CACb,qBAAwB,CAE1B,+CACE,wEAA2E,CAC3E,gFACE,oKAAuK,CAE3K,iFACE,0DAA2D,CAC3D,uDAA0D,CAE5D,wCACE,YAAa,CACb,oBAAqB,CACrB,UAAW,CACX,qPAAsP,CACtP,+EAAgF,CAChF,uEAAwE,CACxE,mBAAoF,CAApF,oFAAoF,CAApF,oBAAoF,CAApF,kBAAoF,CACpF,2EAA8E,CAEhF,8CACE,mBAAoB,CACpB,2BAA4B,CAC5B,iFAAkF,CAClF,eAAgB,CAChB,6EAA8E,CAC9E,eAAgB,CAChB,qBAAwB,CAE1B,8CACE,6EAA8E,CAC9E,gBAAmB,CAErB,6CACE,4EAA6E,CAC7E,+DAAgE,CAChE,yEAA4E,CAC5E,4FACE,+FAAkG,CAEtG,mBACE,wDAAyD,CACzD,8EAA+E,CAC/E,sEAAuE,CACvE,iFAAkF,CAClF,qEAAsE,CACtE,6EAA8E,CAC9E,wEAAyE,CACzE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,iFAAkF,CAClF,+EAAgF,CAChF,+DAAgE,CAChE,yFAA0F,CAC1F,wFAAyF,CACzF,0FAA2F,CAC3F,uFAAwF,CACxF,yFAA0F,CAC1F,0FAA2F,CAC3F,4FAA6F,CAC7F,4FAA6F,CAC7F,wEAAyE,CACzE,+EAAgF,CAChF,2FAA4F,CAC5F,0EAA2E,CAC3E,yEAA0E,CAC1E,kEAAmE,CACnE,+DAAgE,CAChE,qFAAsF,CACtF,6EAA8E,CAC9E,wFAAyF,CACzF,4EAA6E,CAC7E,uFAAwF,CACxF,oEAAqE,CACrE,kEAAmE,CACnE,qEAAsE,CACtE,wEAAyE,CACzE,8DAA+D,CAC/D,uCAAwC,CACxC,uFAAwF,CACxF,2DAA4D,CAC5D,kEAAmE,CACnE,uEAAwE,CACxE,uEAAwE,CACxE,yEAA0E,CAC1E,0EAA2E,CAC3E,wEAAyE,CACzE,iFAAkF,CAClF,mGAAoG,CACpG,qEAAsE,CACtE,8EAA+E,CAC/E,kFAAmF,CACnF,6EAA8E,CAC9E,0EAA2E,CAC3E,yEAA0E,CAC1E,gGAAiG,CACjG,kGAAmG,CACnG,8FAA+F,CAC/F,yEAA0E,CAC1E,oFAAqF,CACrF,yEAA0E,CAC1E,sEAAuE,CACvE,yEAA0E,CAC1E,iBAAkB,CAClB,oBAAqB,CACrB,cAAiB,CACjB,iCACE,yDAA0D,CAC1D,+DAAkE,CAClE,4CACE,8CAAiD,CAEvD,0IAEE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,0DAA2D,CAC3D,sNAAyN,CAE3N,sJAEE,yGAA4G,CAE9G,0TAGE,0GAA2G,CAC3G,+EAAkF,CAEpF,sJAEE,yGAA0G,CAC1G,8EAAiF,CAEnF,2BACE,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,oDAAqD,CACrD,cAAe,CACf,0DAA2D,CAC3D,wDAAyD,CACzD,6CAA8C,CAC9C,kEAAmE,CACnE,WAAc,CACd,2CACE,wDAAyD,CACzD,4DAA6D,CAC7D,8DAAiE,CACnE,mEACE,4GAA6G,CAC7G,iFAAoF,CACtF,sDACE,sBAAuB,CACvB,sDAAyD,CAC3D,6EACE,wDAA2D,CAC7D,8PAEE,mGAAsG,CACxG,mGACE,sGAAyG,CAC3G,6EACE,mBAAsB,CACtB,gNACE,wGAA2G,CAC7G,2FACE,QAAW,CAEjB,uCACE,iBAAoB,CAEtB,kCACE,6NAA8N,CAC9N,yEAA0E,CAC1E,QAAW,CAEb,gCACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAqB,CAEvB,gCACE,+DAAgE,CAChE,6DAAgE,CAChE,0EACE,kFAAqF,CAEzF,yBACE,iBAAkB,CAClB,uCAAwC,CACxC,8CAA+C,CAC/C,cAAe,CACf,sDAAuD,CACvD,4DAA6D,CAC7D,gEAAiE,CACjE,2BAA4B,CAC5B,oDAAuD,CACvD,0CACE,OAAU,CACZ,qDACE,yEAA0E,CAC1E,uEAA0E,CAE9E,8BACE,YAAa,CACb,oBAAqB,CACrB,UAAW,CACX,6MAA8M,CAC9M,uDAAwD,CACxD,gDAAiD,CACjD,kBAAmB,CACnB,qEAAsE,CACtE,WAAc,CACd,wEACE,oBAAqB,CACrB,4EAA+E,CACjF,mFACE,0DAA2D,CAC3D,mBAAoB,CACpB,+EAAkF,CAEtF,mCACE,iBAAkB,CAClB,UAAW,CACX,kEAAmE,CACnE,gBAAiB,CACjB,4DAA6D,CAC7D,qDAAwD,CAE1D,oDACE,8DAAiE,CAEnE,gCAIE,qNAAgE,CAChE,yDAA0D,CAC1D,6DAA8D,CAC9D,kDAAqD,CAEvD,oBACE,+DAAgE,CAChE,yDAA0D,CAC1D,2EAA4E,CAC5E,0EAA2E,CAC3E,2FAA4F,CAC5F,2FAA4F,CAC5F,qFAAsF,CACtF,gGAAiG,CACjG,+DAAgE,CAChE,mBAAoB,CACpB,kBAAqB,CAMvB,wDAHE,YAAa,CACb,kBAKqB,CAHvB,2BACE,qEAEqB,CACrB,6CACE,qFAAwF,CACxF,+CACE,0FAA6F,CACjG,kDACE,0FAA6F,CAC7F,oDACE,+FAAkG,CAExG,0BACE,oEAAuE,CAEzE,+GAIE,8CAAiD,CACjD,2JAIE,8BAAiC,CAErC,2EAEE,qFAAwF,CAE1F,uGAEE,8CAAiD,CACjD,6HAEE,8BAAiC,CAErC,WACE,yEAA0E,CAC1E,gFAAiF,CACjF,wDAAyD,CACzD,sCAAuC,CACvC,mEAAoE,CACpE,wEAAyE,CACzE,uEAAwE,CACxE,qFAAsF,CACtF,uFAAwF,CACxF,wFAAyF,CACzF,sFAAuF,CACvF,sFAAuF,CACvF,gIAAmI,CACnI,sFAAuF,CACvF,0DAA2D,CAC3D,oFAAqF,CACrF,wDAAyD,CACzD,sEAAuE,CACvE,qEAAsE,CACtE,mEAAoE,CACpE,uEAAwE,CACxE,4EAA6E,CAC7E,wEAAyE,CACzE,6CAA8C,CAC9C,6CAA8C,CAC9C,wHAAyH,CACzH,8HAA+H,CAC/H,gIAAiI,CACjI,0IAA2I,CAC3I,4IAA6I,CAC7I,mEAAoE,CACpE,oEAAqE,CACrE,oHAAqH,CACrH,wGAAyG,CACzG,+GAAgH,CAChH,yDAA0D,CAC1D,+BAAgC,CAChC,qCAAsC,CACtC,iFAAkF,CAClF,2FAA4F,CAC5F,qEAAsE,CACtE,6DAA8D,CAC9D,sCAAuC,CACvC,kCAAmC,CACnC,8CAA+C,CAC/C,sCAAuC,CACvC,kEAAmE,CACnE,qEAAsE,CACtE,sDAAuD,CACvD,kEAAmE,CACnE,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,sEAAuE,CACvE,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,mFAAoF,CACpF,uFAAwF,CACxF,gGAAiG,CACjG,8GAA+G,CAC/G,8GAA+G,CAC/G,6FAA8F,CAC9F,uEAAwE,CACxE,oFAAqF,CACrF,0EAA2E,CAC3E,oFAAqF,CACrF,uFAAwF,CACxF,0EAA2E,CAC3E,iFAAkF,CAClF,uEAAwE,CACxE,mFAAoF,CACpF,8DAA+D,CAC/D,qCAAsC,CACtC,oCAAqC,CACrC,+EAAgF,CAChF,oEAAqE,CACrE,mEAAoE,CACpE,0FAA2F,CAC3F,qEAAsE,CACtE,uEAAwE,CACxE,6CAA8C,CAC9C,sEAAuE,CACvE,sFAAuF,CACvF,2EAA4E,CAC5E,0EAA2E,CAC3E,sFAAuF,CACvF,2EAA4E,CAC5E,0EAA2E,CAC3E,YAAa,CACb,WAAY,CACZ,yBAA0B,CAC1B,kCAAmC,CACnC,mCAAoC,CACpC,kDAAqD,CACrD,0BACE,WACE,sFAAyF,CAAE,CAC/F,qCACE,WACE,0FAA2F,CAC3F,oFAAqF,CACrF,kFAAmF,CAGnF,sFAAuF,CAGvF,0EAA2E,CAG3E,oFAAqF,CACrF,wFAAyF,CACzF,0FAA2F,CAC3F,sFAAuF,CAGvF,gFAAiF,CACjF,8EAA+E,CAG/E,8FAA+F,CAC/F,4FApBqF,CAAE,CAqB3F,0BACE,WACE,qCAAsC,CACtC,8CAAiD,CAAE,CAEzD,mBACE,kCAAmC,CACnC,wCAAyC,CACzC,+BAAgC,CAChC,YAAa,CACb,gBAAiB,CACjB,kBAAmB,CACnB,WAAY,CACZ,8CAA+C,CAC/C,0DAA6D,CAC7D,qBACE,YAAa,CACb,kBAAqB,CACvB,oCACE,mBACE,mCAAsC,CAAE,CAE9C,yBACE,eAAkB,CAClB,wDAA2D,CAC3D,0BACE,yBACE,8DAAiE,CAAE,CAEzE,8BACE,YAAa,CACb,MAAO,CACP,kBAAqB,CACrB,0CACE,kEAAqE,CAEzE,6CACE,qQAAsQ,CACtQ,2EAA4E,CAC5E,yEAA0E,CAC1E,qEAAsE,CACtE,aAAgB,CAElB,uBACE,kBAAmB,CACnB,WAAY,CACZ,wDAAyD,CACzD,sDAAuD,CACvD,8DAA+D,CAC/D,gBAAmB,CACnB,YAAiB,CACjB,qCACE,uBACE,eAAkB,CAClB,YAAiB,CAAE,CACvB,iCACE,kBAAqB,CAEzB,yBACE,eAAkB,CAClB,wDAAyD,CACzD,gBAAmB,CACnB,sCACE,gEAAmE,CACrE,oCACE,yBACE,eAAoB,CAAE,CAE5B,+BACE,mFAAoF,CACpF,kBAAqB,CACrB,8DACE,4DAA+D,CAEnE,8BACE,kFAAqF,CACrF,uEACE,mIAAsI,CACxI,yDACE,8FAA+F,CAC/F,wFAA2F,CAC3F,gEACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,yEAA0E,CAC1E,2EAA4E,CAC5E,UAAa,CACf,8FACE,4JAA+J,CAC/J,oGACE,qHAAwH,CAC5H,iGACE,qFAAwF,CAC5F,sFACE,gJAAmJ,CACrJ,yFACE,mJAAsJ,CAE1J,oBACE,aAAc,CACd,gBAAiB,CACjB,mBAAoB,CACpB,yCAA0C,CAC1C,sCAAuC,CACvC,iBAAkB,CAClB,eAAgB,CAChB,gCAAiC,CACjC,2DAA4D,CAC5D,gDAAiD,CACjD,6GAAgH,CAChH,qCACE,oBACE,+CAAkD,CAAE,CACxD,kCACE,kFAAmF,CACnF,+CAAkD,CACpD,mCACE,WAAY,CACZ,eAAkB,CACpB,+BACE,kCAAmC,CACnC,yFAA4F,CAEhG,yBACE,sDAAuD,CACvD,4DAA+D,CAEjE,sKAIE,YAAa,CACb,qBAAsB,CACtB,SAAY,CACZ,8PAIE,MAAO,CACP,4DAA+D,CAEnE,yHAKE,aAAgB,CAChB,yMAKE,eAAgB,CAChB,KAAM,CACN,uDAAwD,CACxD,6DAAgE,CAClE,wNAKE,eAAgB,CAChB,QAAS,CACT,0DAA2D,CAC3D,gEAAmE,CACrE,kOAKE,iBAAkB,CAClB,aAAc,CACd,aAAgB,CAClB,wNAKE,0DAA2D,CAC3D,gEAAmE,CACrE,yMAKE,uDAAwD,CACxD,6DAAgE,CAEpE,oCAEE,cAAe,CACf,sCAAuC,CACvC,iBAAkB,CAClB,eAAgB,CAChB,gCAAmC,CACnC,gDAEE,SAAY,CAEhB,gEAGE,YAAa,CACb,qBAAwB,CAE1B,qBACE,kDAAmD,CACnD,sDAAuD,CACvD,oDAAqD,CACrD,4DAA+D,CAC/D,4GAEE,sEAAyE,CAE7E,4BACE,qMAAsM,CACtM,mEAAsE,CACtE,qDACE,iGAAoG,CACtG,0HAEE,0GAA6G,CAEjH,wTAOE,WAAc,CAEhB,+GAGE,WAAc,CAEhB,yBACE,yLAA0L,CAC1L,gEAAmE,CACnE,oCACE,mGAAsG,CACxG,4CACE,kCAAqC,CACvC,uCACE,sGAAyG,CAC3G,uCACE,sGAAyG,CAC3G,sCACE,yLAA4L,CAC9L,yCACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAC7C,yBACE,4CACE,yLAA4L,CAC9L,+CACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAAE,CACjD,yBACE,4CACE,yLAA4L,CAC9L,+CACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAAE,CACjD,yBACE,4CACE,yLAA4L,CAC9L,+CACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAAE,CACjD,0BACE,4CACE,yLAA4L,CAC9L,+CACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAAE,CACjD,0BACE,6CACE,yLAA4L,CAC9L,gDACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAAE,CAEnD,wBACE,WAAY,CACZ,+DAAgE,CAChE,4GAA+G,CAEjH,uBACE,aAAgB,CAElB,2CACE,kDAAmD,CACnD,sDAAuD,CACvD,oDAAuD,CAEzD,kDACE,qMAAwM,CAE1M,+CACE,yLAA4L,CAE9L,mBACE,cAAiB,CACjB,gCACE,aAAgB,CAEpB,iBACE,kEAAmE,CACnE,iDAAkD,CAClD,gFAAiF,CACjF,6EAA8E,CAC9E,kFAAmF,CACnF,oCAAqC,CACrC,yCAA0C,CAC1C,uDAAwD,CACxD,4DAA6D,CAC7D,2DAA4D,CAC5D,0DAA2D,CAC3D,mFAAoF,CACpF,kFAAmF,CACnF,iFAAkF,CAClF,uGAA0G,CAC1G,2FAA4F,CAC5F,8FAA+F,CAE/F,6FAA8F,CAC9F,yGAA0G,CAC1G,iGAAkG,CAClG,4GAA6G,CAC7G,gGAAiG,CACjG,+FAAgG,CAChG,2EAA4E,CAC5E,4EAA6E,CAC7E,6EAA8E,CAC9E,mFAAoF,CACpF,oEAAqE,CACrE,iEAAkE,CAClE,8MAAmN,CACnN,6CAA8C,CAC9C,kDAAmD,CACnD,gEAAiE,CACjE,qEAAsE,CACtE,4DAA6D,CAC7D,iEAAkE,CAClE,mFAAoF,CACpF,6EAA8E,CAC9E,wEAAyE,CACzE,0EAA2E,CAC3E,2EAA4E,CAC5E,yEAA0E,CAC1E,gEAAiE,CACjE,kCAAmC,CACnC,mFAAoF,CACpF,0EAA2E,CAC3E,qCAAsC,CACtC,wEAAyE,CACzE,0EAA2E,CAC3E,2EAA4E,CAC5E,yEAA0E,CAC1E,0EAA2E,CAC3E,yEAA0E,CAC1E,oFAAqF,CACrF,+CAAgD,CAChD,oDAAqD,CACrD,kEAAmE,CACnE,uEAAwE,CACxE,sEAAuE,CACvE,qEAAsE,CACtE,YAAa,CACb,cAAe,CACf,kBAAmB,CACnB,wBAA2B,CAC3B,oCACE,iBACE,sIAAuI,CACvI,0IAA2I,CAC3I,4IAA6I,CAC7I,wIAAyI,CACzI,wGAAyG,CACzG,mEAAoE,CACpE,2CAA4C,CAC5C,sDAAuD,CACvD,qDAAsD,CACtD,2CAA4C,CAC5C,0CAA2C,CAC3C,4CAA6C,CAC7C,iDAAoD,CAAE,CAC1D,qCACE,iBACE,gGAAiG,CACjG,8FAAiG,CAAE,CACvG,sEACE,uDAA0D,CAC5D,oCACE,uDAAwD,CACxD,6DAAgE,CAClE,6BACE,0FAA2F,CAC3F,4HAA6H,CAC7H,2HAA4H,CAC5H,4FAA6F,CAC7F,qCAAsC,CACtC,eAAgB,CAChB,+CAAgD,CAChD,sBAAuB,CACvB,kEAAmE,CACnE,sDAAyD,CACzD,wEACE,6FAA8F,CAC9F,mGAAoG,CACpG,qFAAwF,CAC1F,yCACE,wCAAyC,CACzC,6CAA8C,CAC9C,iBAAkB,CAClB,eAAkB,CACpB,0MAGE,YAAa,CACb,iBAAoB,CACtB,gDACE,iBAAkB,CAClB,aAAc,CACd,kBAAqB,CACvB,mDACE,YAAa,CACb,eAAgB,CAChB,6BAA8B,CAC9B,kBAAqB,CACvB,oCACE,6BACE,6CAA8C,CAC9C,wCAAyC,CACzC,wCAAyC,CACzC,iBAAkB,CAClB,wBAAyB,CACzB,iNAAoN,CACpN,0MAGE,aAAc,CACd,kBAAqB,CACvB,gDACE,iBAAoB,CACtB,mDACE,mBAAoB,CACpB,eAAkB,CAAE,CAC5B,6BACE,qCAAsC,CACtC,eAAgB,CAChB,yCAA0C,CAC1C,gDAAiD,CAIjD,iMAA2D,CAC3D,kEAAmE,CACnE,sDAAyD,CACzD,oCACE,6BACE,iNAAoN,CAAE,CAC5N,4CACE,kEAAqE,CACvE,8BACE,2FAA8F,CAElG,sBACE,4CAA6C,CAC7C,wBAAyB,CACzB,kDAAqD,CAEvD,2CACE,yEAA0E,CAC1E,uEAAwE,CACxE,iEAAoE,CAEtE,0FACE,mFAAsF,CAExF,kCACE,YAAa,CACb,kBAAmB,CACnB,mEAAoE,CACpE,iEAAoE,CACpE,oCACE,2DAA4D,CAC5D,kBAAqB,CACrB,oDACE,wEAA2E,CAC/E,qDACE,oEAAuE,CAE3E,8BACE,oDAAqD,CACrD,0DAA6D,CAE/D,sCACE,uFAAwF,CACxF,6FAA8F,CAC9F,6GAA8G,CAC9G,mHAAoH,CACpH,uGAAwG,CACxG,6GAAgH,CAElH,mCACE,oFAAqF,CACrF,0FAA2F,CAC3F,0GAA2G,CAC3G,gHAAiH,CACjH,oGAAqG,CACrG,0GAA6G,CAE/G,yBACE,4CACE,uFAAwF,CACxF,6FAA8F,CAC9F,6GAA8G,CAC9G,mHAAoH,CACpH,uGAAwG,CACxG,6GAAgH,CAClH,yCACE,oFAAqF,CACrF,0FAA2F,CAC3F,0GAA2G,CAC3G,gHAAiH,CACjH,oGAAqG,CACrG,0GAA6G,CAAE,CAEnH,yBACE,4CACE,uFAAwF,CACxF,6FAA8F,CAC9F,6GAA8G,CAC9G,mHAAoH,CACpH,uGAAwG,CACxG,6GAAgH,CAClH,yCACE,oFAAqF,CACrF,0FAA2F,CAC3F,0GAA2G,CAC3G,gHAAiH,CACjH,oGAAqG,CACrG,0GAA6G,CAAE,CAEnH,yBACE,4CACE,uFAAwF,CACxF,6FAA8F,CAC9F,6GAA8G,CAC9G,mHAAoH,CACpH,uGAAwG,CACxG,6GAAgH,CAClH,yCACE,oFAAqF,CACrF,0FAA2F,CAC3F,0GAA2G,CAC3G,gHAAiH,CACjH,oGAAqG,CACrG,0GAA6G,CAAE,CAEnH,0BACE,4CACE,uFAAwF,CACxF,6FAA8F,CAC9F,6GAA8G,CAC9G,mHAAoH,CACpH,uGAAwG,CACxG,6GAAgH,CAClH,yCACE,oFAAqF,CACrF,0FAA2F,CAC3F,0GAA2G,CAC3G,gHAAiH,CACjH,oGAAqG,CACrG,0GAA6G,CAAE,CAEnH,0BACE,6CACE,uFAAwF,CACxF,6FAA8F,CAC9F,6GAA8G,CAC9G,mHAAoH,CACpH,uGAAwG,CACxG,6GAAgH,CAClH,0CACE,oFAAqF,CACrF,0FAA2F,CAC3F,0GAA2G,CAC3G,gHAAiH,CACjH,oGAAqG,CACrG,0GAA6G,CAAE,CAEnH,cACE,uDAAwD,CACxD,gIAAiI,CACjI,gIAAiI,CACjI,yDAA0D,CAC1D,+EAAgF,CAChF,gEAAiE,CACjE,kEAAmE,CACnE,mEAAoE,CACpE,iEAAkE,CAClE,8DAA+D,CAC/D,+DAAgE,CAChE,gEAAiE,CACjE,6EAA8E,CAC9E,6CAA8C,CAC9C,4CAA6C,CAC7C,0CAA2C,CAC3C,+CAAgD,CAChD,+CAAgD,CAChD,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAiD,CACjD,6CAA8C,CAC9C,6CAA8C,CAC9C,8CAA+C,CAC/C,2CAA4C,CAC5C,iEAAkE,CAClE,qHAAsH,CACtH,4DAA6D,CAC7D,6EAA8E,CAC9E,kEAAmE,CACnE,8DAA+D,CAC/D,iBAAkB,CAClB,uCAAwC,CACxC,uCAAwC,CACxC,uCAAwC,CACxC,yCAA4C,CAC5C,8BACE,uCAAwC,CACxC,yCAA0C,CAC1C,0CAA2C,CAC3C,wCAA2C,CAC7C,8BACE,6BAA8B,CAC9B,6BAAgC,CAClC,4CACE,QAAS,CACT,QAAS,CACT,+KAAkL,CACpL,+CACE,KAAM,CACN,QAAS,CACT,wLAA2L,CAC7L,6CACE,OAAQ,CACR,OAAQ,CACR,kLAAqL,CACvL,8CACE,OAAQ,CACR,MAAO,CACP,qLAAwL,CAE5L,uBACE,iBAAkB,CAClB,iLAAkL,CAClL,8DAAiE,CACjE,mCACE,wDAA2D,CAC7D,oCACE,iBAAkB,CAClB,sCAAuC,CACvC,0CAA6C,CAC7C,sCACE,kEAAqE,CAE3E,qBACE,iBAAkB,CAClB,uCAAwC,CACxC,yCAA0C,CAC1C,mBAAoB,CACpB,4DAA6D,CAC7D,gDAAmD,CAErD,oBACE,oBAAuB,CAEzB,sBACE,iDAAoD,CAEtD,eACE,qDAAsD,CACtD,kFAAmF,CACnF,yDAA0D,CAC1D,kFAAmF,CACnF,wDAAyD,CACzD,gEAAiE,CACjE,qEAAsE,CACtE,wCAAyC,CACzC,oEAAqE,CACrE,8FAA+F,CAC/F,qFAAsF,CACtF,qFAAsF,CACtF,mFAAoF,CACpF,mFAAoF,CACpF,mFAAoF,CACpF,iFAAkF,CAClF,2EAA4E,CAC5E,4EAA6E,CAC7E,uFAAwF,CACxF,sFAAuF,CACvF,2EAA4E,CAC5E,4EAA6E,CAC7E,+DAAgE,CAChE,2EAA4E,CAC5E,uEAAwE,CACxE,+DAAgE,CAChE,YAAa,CACb,eAAgB,CAChB,sCAAuC,CACvC,+BAAgC,CAChC,2BAA8B,CAC9B,uBACE,oEAAuE,CACvE,mDACE,2DAA8D,CAChE,+CACE,uDAA0D,CAC9D,uBACE,oEAAuE,CACzE,qDACE,YAAa,CACb,kBAAmB,CACnB,sBAAuB,CACvB,6DAAgE,CAClE,mDACE,2DAA4D,CAC5D,oDAAqD,CACrD,iBAAoB,CACtB,wDACE,eAAoB,CACtB,mDACE,eAAkB,CAClB,YAAe,CACf,iBAAoB,CACtB,oDACE,oBAAqB,CACrB,4DAA+D,CAC/D,sEACE,iEAAkE,CAClE,eAAkB,CACtB,sGAEE,eAAoB,CACtB,+BACE,sBAAyB,CACzB,2DACE,YAAa,CACb,iBAAoB,CACtB,mDACE,YAAe,CACf,eAAoB,CACtB,sDACE,YAAe,CACf,eAAoB,CACxB,2DACE,0CAA6C,CAC/C,4BACE,mGAAoG,CACpG,uFAAwF,CACxF,mGAAsG,CACxG,4BACE,mGAAoG,CACpG,uFAAwF,CACxF,mGAAsG,CACxG,2BACE,kGAAmG,CACnG,sFAAyF,CAE7F,4BACE,qBAAsB,CACtB,eAAoB,CACpB,0CACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAqB,CAEzB,uBACE,eAAkB,CAClB,YAAe,CACf,gBAAiB,CACjB,qBAAwB,CAE1B,4BACE,yDAA0D,CAC1D,8CAAiD,CAEnD,oBACE,iBAAkB,CAClB,eAAkB,CAClB,YAAe,CACf,iBAAkB,CAClB,wCAAyC,CACzC,2DAA8D,CAC9D,2BACE,iBAAkB,CAClB,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,UAAW,CACX,mEAAoE,CACpE,kDAAqD,CAEzD,0BACE,iBAAkB,CAClB,KAAM,CACN,MAAO,CACP,8CAA+C,CAC/C,iEAAoE,CAEtE,YACE,+EAAgF,CAChF,0EAA2E,CAC3E,uDAAwD,CACxD,oEAAqE,CACrE,4DAA6D,CAC7D,gEAAiE,CACjE,yCAA0C,CAC1C,sDAAuD,CACvD,sDAAuD,CACvD,kEAAmE,CACnE,6DAA8D,CAC9D,YAAa,CACb,8BAA+B,CAC/B,mCAAoC,CACpC,kBAAmB,CACnB,mBAAsB,CAExB,mBACE,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAiD,CACjD,qCAAwC,CAE1C,mBACE,8CAAiD,CACjD,+BACE,6DAAgE,CAClE,8BACE,8DAAiE,CAErE,yBACE,aAAc,CACd,kDAAmD,CACnD,2CAA8C,CAEhD,uDAEE,cAAiB,CAEnB,0HAGE,oEAAqE,CACrE,kBAAqB,CAEvB,mBACE,gFAAiF,CACjF,iFAAkF,CAClF,qFAAsF,CACtF,sFAAuF,CACvF,+FAAgG,CAChG,mGAAoG,CACpG,sGAAuG,CACvG,kFAAmF,CACnF,0EAA2E,CAC3E,qFAAsF,CACtF,yEAA0E,CAC1E,6CAA8C,CAC9C,4DAA6D,CAC7D,6DAA8D,CAC9D,0EAA2E,CAC3E,0CAA2C,CAC3C,wEAAyE,CACzE,uEAAwE,CACxE,8EAA+E,CAC/E,mFAAoF,CACpF,kFAAmF,CACnF,iBAAkB,CAClB,YAAa,CACb,iKAAoK,CACpK,yBACE,mHAAsH,CAE1H,yBACE,MAAS,CACT,+DACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,mBAAoB,CACpB,UAAa,CACf,gCACE,oHAAuH,CACzH,+BACE,qIAAwI,CAC1I,qEACE,mFAAsF,CACxF,sCACE,0HAA2H,CAC3H,0HAA6H,CAEjI,yBACE,iBAAkB,CAClB,OAAQ,CACR,yCAA0C,CAC1C,2CAA4C,CAC5C,gEAAmE,CAErE,+BACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,iBAAkB,CAClB,UAAW,CACX,wDAAyD,CACzD,iNAAkN,CAClN,QAAW,CAEb,8BACE,YAAa,CACb,6DAA8D,CAC9D,2DAA8D,CAC9D,kCACE,kEAAqE,CACvE,2CACE,uFAAwF,CACxF,qFAAwF,CAE5F,wBACE,YAAe,CAEjB,0BACE,YAAa,CACb,kBAAqB,CAEvB,aACE,kCAAmC,CACnC,wEAAyE,CACzE,gEAAiE,CACjE,2EAA4E,CAC5E,+DAAgE,CAChE,uEAAwE,CACxE,8DAA+D,CAC/D,sEAAuE,CACvE,kEAAmE,CACnE,6EAA8E,CAC9E,4EAA6E,CAC7E,gFAAiF,CACjF,kFAAmF,CACnF,mFAAoF,CACpF,iFAAkF,CAClF,yDAA0D,CAC1D,2FAA4F,CAC5F,2FAA4F,CAC5F,4FAA6F,CAC7F,gGAAiG,CACjG,yFAA0F,CAC1F,0FAA2F,CAC3F,8FAA+F,CAC/F,sFAAuF,CACvF,+DAAgE,CAChE,uFAAwF,CACxF,iFAAkF,CAClF,gEAAiE,CACjE,mEAAoE,CACpE,+CAAgD,CAChD,4DAA6D,CAC7D,+CAAgD,CAChD,iDAAkD,CAClD,gDAAiD,CACjD,gDAAiD,CACjD,sIAAuI,CACvI,gFAAiF,CACjF,qEAAsE,CACtE,oEAAqE,CACrE,qEAAsE,CACtE,gFAAiF,CACjF,2EAA4E,CAC5E,sEAAuE,CACvE,qEAAsE,CACtE,oFAAqF,CACrF,gEAAiE,CACjE,iFAAkF,CAClF,8DAA+D,CAC/D,4DAA6D,CAC7D,+DAAgE,CAChE,kEAAmE,CACnE,wDAAyD,CACzD,iFAAkF,CAClF,iEAAkE,CAClE,mEAAoE,CACpE,gFAAiF,CACjF,oEAAqE,CACrE,kEAAmE,CACnE,iEAAkE,CAClE,yEAA0E,CAC1E,qEAAsE,CACtE,iEAAkE,CAClE,oCAAqC,CACrC,2EAA4E,CAC5E,6FAA8F,CAC9F,6FAA8F,CAC9F,+DAAgE,CAChE,4CAA6C,CAC7C,oEAAqE,CACrE,oEAAqE,CACrE,+EAAgF,CAChF,8CAA+C,CAC/C,iFAAkF,CAClF,sEAAuE,CACvE,sEAAuE,CACvE,wFAAyF,CACzF,6EAA8E,CAC9E,6EAA8E,CAC9E,gHAAiH,CACjH,wEAAyE,CACzE,4EAA6E,CAC7E,iEAAkE,CAClE,sCAAuC,CACvC,8CAA+C,CAC/C,uIAA0I,CAC1I,8EAA+E,CAC/E,mEAAoE,CACpE,oFAAqF,CACrF,sEAAuE,CACvE,kFAAmF,CACnF,8EAA+E,CAC/E,qFAAsF,CACtF,yFAA0F,CAC1F,2FAA4F,CAC5F,uFAAwF,CACxF,wEAAyE,CACzE,mFAAoF,CACpF,wEAAyE,CACzE,6EAA8E,CAC9E,wEAAyE,CACzE,8FAA+F,CAC/F,uFAAwF,CACxF,0HAA2H,CAC3H,qEAAsE,CACtE,wEAAyE,CACzE,iBAAkB,CAClB,oBAAqB,CACrB,UAAa,CACb,2BACE,wDAAyD,CACzD,8DAAiE,CACjE,sCACE,6CAAgD,CAEtD,wCACE,0CAA6C,CAE/C,qBACE,iBAAkB,CAClB,YAAa,CACb,kBAAmB,CACnB,6BAA8B,CAC9B,UAAW,CACX,8CAA+C,CAC/C,yKAA0K,CAC1K,8CAA+C,CAC/C,kDAAmD,CACnD,kDAAmD,CACnD,uCAAwC,CACxC,kBAAmB,CACnB,cAAe,CACf,4DAA6D,CAC7D,WAAc,CACd,iEACE,4FAA6F,CAC7F,mBAAsB,CACtB,+EACE,QAAW,CACf,4BACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,4DAA6D,CAC7D,8NAAiO,CACnO,kCACE,6GAAgH,CAClH,2EACE,6GAA8G,CAC9G,gFAAmF,CACrF,2EACE,8GAA+G,CAC/G,iFAAoF,CACtF,2CACE,kHAAmH,CACnH,qFAAwF,CAC1F,uCACE,qEAAwE,CAC1E,oCACE,mCAAoC,CACpC,qCAAsC,CACtC,sCAAyC,CACzC,uDACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAmB,CACnB,iBAAkB,CAClB,WAAc,CAClB,gDACE,4DAA6D,CAC7D,0DAA2D,CAC3D,gBAAmB,CACrB,iDACE,8CAAiD,CACnD,4EACE,yEAA4E,CAEhF,6BACE,0DAA2D,CAC3D,wDAA2D,CAE7D,+DACE,2FAA8F,CAEhG,0BACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAqB,CAEvB,6BACE,YAAa,CACb,MAAO,CACP,cAAe,CACf,kBAAmB,CACnB,0BAA2B,CAC3B,WAAY,CACZ,sDAAuD,CACvD,kBAAqB,CACrB,+CACE,4EAA+E,CACjF,gDACE,+EAAoF,CACtF,8CACE,sEAAuE,CACvE,4EAA+E,CACjF,wEACE,4DAAiE,CAErE,oDACE,oEAAuE,CAEzE,2BACE,YAAa,CACb,0DAA6D,CAE/D,+BACE,0DAA2D,CAC3D,WAAY,CACZ,wDAAyD,CACzD,sEAAuE,CACvE,0DAA2D,CAC3D,8DAA+D,CAG/D,qBAA4B,CAC5B,4DAA6D,CAC7D,aAAgB,CAChB,qCACE,yEAA4E,CAEhF,mBACE,iBAAkB,CAClB,iCAAkC,CAClC,wCAAyC,CACzC,cAAe,CACf,gDAAiD,CACjD,sDAAuD,CACvD,0DAA2D,CAC3D,2BAA4B,CAC5B,8CAAiD,CACjD,oCACE,OAAU,CACZ,yCACE,KAAM,CACN,iEAAoE,CAExE,4BACE,QAAW,CAEb,2BACE,YAAe,CACf,sFACE,iHAAoH,CAExH,wBACE,iBAAkB,CAClB,0CAA2C,CAC3C,qLAAsL,CACtL,iDAAkD,CAClD,qDAAsD,CACtD,qDAAsD,CACtD,0CAA2C,CAC3C,eAAgB,CAChB,kBAAmB,CACnB,4BAA6B,CAC7B,WAAc,CACd,+FACE,0GAA2G,CAC3G,oBAAuB,CACzB,+DACE,sEAAyE,CAC3E,+IAEE,iBAAkB,CAClB,sEAAyE,CAC3E,kCACE,wCAAyC,CACzC,6CAA8C,CAC9C,oDAAqD,CACrD,4EAA6E,CAC7E,8GAA+G,CAC/G,8GAA+G,CAC/G,WAAc,CAChB,oCACE,8EAA+E,CAC/E,8EAA+E,CAC/E,gHAAiH,CACjH,gHAAiH,CACjH,YAAa,CACb,sBAAuB,CACvB,2DAA8D,CAC9D,0CACE,+FAAkG,CACpG,0CACE,+FAAkG,CACtG,sCACE,8FAA+F,CAC/F,mHAAsH,CACxH,yCACE,kBAAqB,CACrB,0DACE,wCAA2C,CAC7C,4DACE,kBAAqB,CACzB,gHACE,oDAAqD,CACrD,mBAAoB,CACpB,yEAA4E,CAEhF,6BACE,iBAAkB,CAClB,aAAc,CACd,8DAA+D,CAC/D,kBAAqB,CAEvB,oCACE,aAAc,CACd,qEAAsE,CACtE,6DAA8D,CAC9D,sDAAyD,CAE3D,6BACE,iBAAkB,CAClB,2CAA4C,CAC5C,+CAAgD,CAChD,sDAAuD,CACvD,+CAAgD,CAChD,oEAAuE,CAEzE,oCACE,YAAa,CACb,kBAAmB,CACnB,+DAAkE,CAEpE,+BACE,4DAA6D,CAC7D,wBAA2B,CAE7B,kDACE,kEAAqE,CAEvE,0BACE,6LAAgM,CAElM,+BAIE,iNAA+D,CAC/D,wDAAyD,CACzD,4DAA6D,CAC7D,iDAAoD,CAEtD,kBACE,sEAAuE,CACvE,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,qFAAsF,CACtF,iEAAkE,CAClE,sEAAuE,CACvE,8EAA+E,CAC/E,6EAA8E,CAC9E,gGAAiG,CACjG,4FAA6F,CAC7F,yEAA0E,CAC1E,4FAA6F,CAC7F,yEAA0E,CAC1E,4FAA6F,CAC7F,wFAAyF,CACzF,0EAA2E,CAC3E,6FAA8F,CAC9F,yFAA0F,CAC1F,kEAAmE,CACnE,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,kEAAmE,CACnE,kEAAmE,CACnE,6EAA8E,CAC9E,4EAA+E,CAEjF,6BACE,aAAc,CACd,UAAW,CACX,yMAA0M,CAC1M,sDAAuD,CACvD,0DAA2D,CAC3D,+CAAgD,CAChD,eAAgB,CAChB,oEAAqE,CACrE,WAAc,CACd,0CACE,mGAAoG,CACpG,6GAA8G,CAC9G,yFAA4F,CAC9F,mCACE,oBAAqB,CACrB,yGAA0G,CAC1G,qFAAwF,CAC1F,mCACE,+FAAgG,CAChG,yGAA0G,CAC1G,qFAAwF,CAC1F,oCACE,gGAAiG,CACjG,0GAA2G,CAC3G,sFAAyF,CAE7F,yBACE,yLAA0L,CAC1L,kDAAmD,CACnD,sDAAuD,CACvD,2CAA8C,CAEhD,sDACE,+DAAkE,CAEpE,eACE,qEAAsE,CACtE,2BAA4B,CAC5B,4BAA6B,CAC7B,gEAAiE,CACjE,wCAAyC,CACzC,oCAAqC,CACrC,wCAAyC,CACzC,iDAAkD,CAClD,mEAAwE,CACxE,wDAAyD,CACzD,mEAAwE,CACxE,wCAAyC,CACzC,2DAA4D,CAC5D,4CAA6C,CAC7C,wDAAyD,CACzD,sDAAuD,CACvD,0CAA2C,CAC3C,0EAA2E,CAC3E,qDAAsD,CACtD,0GAA6G,CAC7G,0GAA6G,CAC7G,0GAA6G,CAC7G,wGAA2G,CAC3G,wGAA2G,CAC3G,wGAA2G,CAC3G,wGAA2G,CAC3G,0CAA2C,CAC3C,0CAA2C,CAC3C,2CAA4C,CAC5C,sCAAuC,CACvC,4CAAkD,CAClD,sCAAuC,CACvC,4CAAsD,CACtD,sCAAuC,CACvC,4CAA6C,CAC7C,4CAA6C,CAC7C,6CAA8C,CAC9C,wCAAyC,CACzC,8CAAoD,CACpD,wCAAyC,CACzC,8CAAwD,CACxD,wCAAyC,CACzC,0CAA2C,CAC3C,iBAAkB,CAClB,iCAAkC,CAClC,mCAAoC,CACpC,eAAgB,CAChB,sDAAuD,CACvD,gDAAiD,CACjD,sBAAyB,CACzB,sBACE,aAAc,CACd,2CAA4C,CAC5C,0DAA2D,CAC3D,6CAAgD,CAClD,qBACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,aAAc,CACd,UAAW,CACX,8OAAkP,CAClP,6DAA8D,CAC9D,yPAA4P,CAC9P,2BACE,0EAA6E,CAC/E,sDACE,iCAAkC,CAClC,4FAA+F,CACjG,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,6BACE,8DAAiE,CACnE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,8BACE,iEAAoE,CACtE,+BACE,kEAAqE,CACvE,6BACE,gEAAmE,CACrE,6BACE,gEAAmE,CACrE,6BACE,gEAAmE,CACrE,4BACE,+DAAkE,CACpE,4BACE,+DAAkE,CACpE,4BACE,+DAAkE,CACpE,4BACE,+DAAkE,CAEtE,iCACE,GACE,2BAA8B,CAChC,IACE,0BAA6B,CAC/B,GACE,0BAA6B,CAAE,CAEnC,sBACE,wDAAyD,CACzD,4DAA6D,CAC7D,gEAAiE,CACjE,iBAAkB,CAClB,oCAAqC,CACrC,UAAW,CACX,2CAA8C,CAC9C,4BACE,6CAAgD,CAEpD,aACE,4DAA6D,CAC7D,+DAAgE,CAChE,yCAA0C,CAC1C,2FAA4F,CAC5F,4FAA6F,CAC7F,kFAAmF,CACnF,iGAAkG,CAClG,6DAA8D,CAC9D,gEAAiE,CACjE,0DAA2D,CAC3D,uCAAwC,CACxC,wCAAyC,CACzC,4EAA6E,CAC7E,yCAA0C,CAC1C,yEAA0E,CAC1E,gGAAiG,CACjG,yDAA0D,CAC1D,4DAA6D,CAC7D,0CAA2C,CAC3C,0GAA2G,CAC3G,0DAA2D,CAC3D,6DAA8D,CAC9D,uGAA0G,CAC1G,gCAAiC,CACjC,iCAAkC,CAClC,oDAAqD,CACrD,0EAA2E,CAC3E,qCAAsC,CACtC,qCAAsC,CACtC,qEAAsE,CACtE,qIAEgD,CAChD,iFAAkF,CAClF,iFAAkF,CAClF,kIAEiD,CACjD,6DAA8D,CAC9D,sDAAuD,CACvD,mDAAoD,CACpD,oKAAyK,CACzK,iDAAkD,CAClD,kDAAmD,CACnD,gEAAiE,CACjE,qEAAsE,CACtE,gEAAiE,CACjE,qEAAsE,CACtE,YAAe,CAEjB,mBACE,iBAAkB,CAClB,WAAc,CAEhB,mBACE,gDAAiD,CACjD,sDAAyD,CAE3D,yBACE,iBAAkB,CAClB,6CAAgD,CAChD,gCACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,qXAA4X,CAC5X,kEAAqE,CAEzE,oBACE,wCAAyC,CACzC,6CAA8C,CAC9C,aAAgB,CAElB,mBACE,iBAAkB,CAClB,KAAM,CACN,mCAAoC,CACpC,UAAa,CACb,+BACE,0GAA6G,CAC/G,+BACE,iGAAkG,CAClG,mGAAsG,CACxG,8BACE,gGAAiG,CACjG,kGAAqG,CAEzG,wBACE,iBAAkB,CAClB,sCAAuC,CACvC,MAAO,CACP,0CAA2C,CAC3C,4CAA6C,CAC7C,+DAAgE,CAChE,yDAA0D,CAC1D,+DAAkE,CAEpE,yBACE,iBAAkB,CAClB,uCAAwC,CACxC,gEAAmE,CAErE,oBACE,iBAAkB,CAClB,kCAAmC,CACnC,oCAAqC,CACrC,sCAAuC,CACvC,wCAAyC,CACzC,cAAe,CACf,2DAA4D,CAC5D,qDAAsD,CACtD,+CAAgD,CAChD,gGAAoG,CACpG,0BACE,2EAA8E,CAChF,0BACE,2EAA4E,CAC5E,SAAY,CACd,2BACE,4EAA+E,CAEnF,oBACE,iDAAoD,CACpD,kCACE,kCAAmC,CACnC,iBAAkB,CAClB,KAAM,CACN,gDAAiD,CACjD,qDAAsD,CACtD,wHAA4H,CAC5H,oDACE,kBAAqB,CACvB,0DACE,iBAAkB,CAClB,SAAY,CAChB,uCACE,sDAAyD,CAE7D,sBACE,YAAa,CACb,qDAAwD,CACxD,yCACE,qCAAsC,CACtC,yDAA4D,CAEhE,cACE,sCAAuC,CACvC,qEAAyE,CACzE,6DAA8D,CAC9D,0CAA2C,CAC3C,6GAAgH,CAChH,mDAAoD,CACpD,oDAAqD,CACrD,0DAA2D,CAC3D,mEAAoE,CACpE,mEAAoE,CACpE,mEAAoE,CACpE,mEAAoE,CACpE,4DAA6D,CAC7D,6DAA8D,CAC9D,yEAA0E,CAC1E,mEAAoE,CACpE,oEAAqE,CACrE,uFAAwF,CACxF,4EAA6E,CAC7E,oEAAqE,CACrE,qEAAsE,CACtE,4EAA6E,CAC7E,iBAAkB,CAClB,oBAAqB,CACrB,gCAAiC,CACjC,kCAAmC,CACnC,eAAgB,CAChB,0IAA+I,CAC/I,sBACE,4DAA+D,CACjE,sBACE,4DAA+D,CACjE,sBACE,4DAA+D,CACjE,sBACE,4DAA+D,CAEnE,uCACE,GACE,sBAAyB,CAC3B,IACE,yBAA4B,CAC9B,GACE,wBAA6B,CAAE,CAEnC,uBACE,iBAAkB,CAClB,yCAA0C,CAC1C,2CAA4C,CAC5C,4BAA6B,CAC7B,8FAAiG,CAEnG,yCACE,GACE,sBAAyB,CAC3B,GACE,yBAA4B,CAAE,CAElC,6BACE,iBAAkB,CAClB,gDAAiD,CACjD,kDAAmD,CACnD,0BAA2B,CAC3B,UAAW,CACX,iBAAkB,CAClB,qIAAsI,CACtI,oGAAuG,CAEzG,+CACE,GACE,uBAA0B,CAC5B,GACE,yBAA4B,CAAE,CAElC,yBACE,iBAAkB,CAClB,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,gGAAmG,CACnG,+BACE,iBAAkB,CAClB,0DAA+D,CAC/D,OAAQ,CACR,6CAA8C,CAC9C,+CAAgD,CAChD,UAAW,CACX,uEAAwE,CACxE,iBAAkB,CAClB,0BAA6B,CAEjC,2CACE,GACE,sBAAyB,CAC3B,IACE,yBAA4B,CAC9B,GACE,wBAA4B,CAAE,CAElC,yBACE,iBAAkB,CAClB,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,gGAAmG,CACnG,+BACE,iBAAkB,CAClB,0DAA+D,CAC/D,OAAQ,CACR,6CAA8C,CAC9C,+CAAgD,CAChD,UAAW,CACX,uEAAwE,CACxE,iBAAkB,CAClB,0BAA6B,CAEjC,2CACE,GACE,sBAAyB,CAC3B,MACE,yBAA4B,CAC9B,GACE,wBAA4B,CAAE,CAElC,aACE,sDAAuD,CACvD,6EAA+E,CAC/E,oEAAqE,CACrE,uEAAyE,CACzE,2CAA4C,CAC5C,0DAA2D,CAC3D,uFAA0F,CAC1F,2FAA4F,CAC5F,8GAA+G,CAC/G,6EAA8E,CAC9E,qFAAsF,CACtF,kFAAmF,CACnF,yFAA0F,CAC1F,qGAAsG,CACtG,mFAAoF,CACpF,+EAAgF,CAChF,sFAAuF,CACvF,+FAAkG,CAClG,4EAA6E,CAC7E,8DAA+D,CAC/D,iHAAkH,CAClH,+EAAgF,CAChF,oHAAuH,CACvH,2EAA4E,CAC5E,qFAAsF,CACtF,8EAA+E,CAC/E,wEAAyE,CACzE,gEAAiE,CACjE,mJAAoJ,CACpJ,8DAA+D,CAC/D,6DAA8D,CAC9D,iBAAkB,CAClB,oBAAqB,CACrB,iCAAkC,CAClC,sCAAuC,CACvC,0CAA2C,CAC3C,qBAAsB,CACtB,cAAiB,CAEnB,oBACE,iBAAkB,CAClB,cAAe,CACf,SAAY,CACZ,+CACE,2HAA4H,CAC5H,sEAAyE,CAC3E,gDACE,sDAAyD,CAC3D,iDACE,4EAA+E,CAC/E,wDACE,oFAAuF,CAC3F,sCACE,YAAe,CACjB,sDACE,0DAA6D,CAC/D,iFACE,YAAa,CACb,iBAAoB,CACtB,2CACE,YAAe,CACjB,6BACE,kBAAqB,CACrB,iDACE,uDAAwD,CACxD,kBAAqB,CACvB,kDACE,kBAAmB,CACnB,6EAAgF,CAChF,yDACE,qFAAwF,CAEhG,qBACE,iBAAkB,CAClB,oBAAqB,CACrB,uCAAwC,CACxC,yCAA0C,CAC1C,4DAA6D,CAC7D,sDAAyD,CACzD,4BACE,iBAAkB,CAClB,2CAA4C,CAC5C,6CAA8C,CAC9C,aAAc,CACd,+CAAgD,CAChD,iDAAkD,CAClD,UAAW,CACX,oEAAqE,CACrE,8DAA+D,CAC/D,wDAAyD,CACzD,yDAA4D,CAEhE,0BACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,0CAA2C,CAC3C,YAAa,CACb,kBAAmB,CACnB,mDAAoD,CACpD,4CAA+C,CAEjD,oBACE,oBAAqB,CACrB,mDAAoD,CACpD,sCAAuC,CACvC,kBAAqB,CAEvB,kBACE,4EAA+E,CAC/E,iCACE,qDAAwD,CAE5D,8BACE,wEAAyE,CACzE,gFAAiF,CACjF,gFAAiF,CACjF,6CAA8C,CAC9C,2EAA4E,CAC5E,4EAA6E,CAC7E,4FAA6F,CAC7F,uEAAwE,CACxE,mEAAoE,CACpE,oEAAqE,CACrE,sEAAuE,CACvE,0EAA2E,CAC3E,uEAAwE,CACxE,qEAAsE,CACtE,yEAA0E,CAC1E,kFAAmF,CACnF,oFAAqF,CACrF,qFAAsF,CACtF,mFAAoF,CACpF,wDAAyD,CACzD,wCAAyC,CACzC,0CAA2C,CAC3C,2CAA4C,CAC5C,yCAA0C,CAC1C,uEAAwE,CACxE,uEAAwE,CACxE,0EAA2E,CAC3E,oFAAqF,CACrF,8CAA+C,CAC/C,6CAA8C,CAC9C,+EAAgF,CAChF,kFAAmF,CACnF,kFAAmF,CACnF,qFAAsF,CACtF,4FAA+F,CAC/F,+FAAkG,CAClG,6EAAwF,CACxF,2FAA4F,CAC5F,0FAA2F,CAC3F,+FAAgG,CAChG,8FAA+F,CAC/F,4FAA6F,CAC7F,wEAAyE,CACzE,mDAAoD,CACpD,gDAAiD,CACjD,8EAA+E,CAC/E,gDAAiD,CACjD,yEAA0E,CAC1E,gFAAiF,CACjF,gLAAiL,CACjL,qDAAsD,CACtD,qDAAwD,CACxD,qCACE,8BACE,4FAA6F,CAC7F,0FAA2F,CAG3F,sIAAuI,CACvI,oIAJ6F,CAAE,CAMrG,sBACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gDAAiD,CACjD,kDAAmD,CACnD,mDAAoD,CACpD,iDAAkD,CAClD,YAAa,CACb,WAAc,CACd,2BACE,UAAW,CACX,WAAY,CACZ,cAAe,CACf,gBAAiB,CACjB,kBAAmB,CACnB,kBAAqB,CACvB,wCACE,iBAAkB,CAClB,UAAW,CACX,WAAY,CACZ,cAAe,CACf,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CACpD,4BACE,YAAa,CACb,iBAAoB,CACtB,4BACE,aAAgB,CAChB,0CACE,oHAAuH,CAC3H,+CACE,YAAe,CACjB,0DACE,mHAAsH,CACxH,mGAEE,mFAAsF,CACxF,0CACE,wFAA2F,CAC3F,8EACE,eAAkB,CACpB,6DACE,uHAA0H,CAC9H,0DACE,YAAa,CACb,yBAA0B,CAC1B,WAAY,CACZ,6BAA8B,CAC9B,+DAAgE,CAChE,iMAAoM,CACpM,4DACE,6MAAgN,CAChN,uEACE,uGAA0G,CAChH,mCACE,+FAAgG,CAChG,qGAAsG,CACtG,qGAAsG,CACtG,2GAA4G,CAC5G,uCAA0C,CAC1C,uDACE,sEAAuE,CACvE,yEAA4E,CAC9E,oEACE,sFAAyF,CAC7F,0CACE,eAAkB,CACpB,mCACE,oGAAqG,CACrG,aAAc,CACd,+DAAgE,CAChE,yCAA2C,CAC3C,iBAAoB,CACpB,qCACE,aAAgB,CAClB,0CACE,eAAiB,CACjB,eAAgB,CAChB,wBAA2B,CAC/B,sCACE,iCAAoC,CACtC,qCACE,kCAAqC,CACvC,kCACE,kGAAmG,CACnG,sGAAuG,CACvG,wGAAyG,CACzG,oGAAqG,CACrG,QAAW,CACX,0GACE,yCAA4C,CAChD,6DACE,2EAA4E,CAC5E,0EAA2E,CAC3E,gEAAmE,CACrE,4BACE,iBAAoB,CACpB,kCACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,UAAW,CACX,QAAS,CACT,+GAAkH,CACpH,0CACE,+FAAkG,CAClG,gDACE,6CAAgD,CACpD,0EACE,sDAAuD,CACvD,eAAgB,CAChB,UAAW,CACX,4BAA+B,CACnC,kDACE,4CAA6C,CAC7C,8CAA+C,CAC/C,+CAAgD,CAChD,6CAA8C,CAC9C,kCAAmC,CACnC,iCAAkC,CAClC,aAAc,CACd,uDAAwD,CACxD,eAAgB,CAChB,kBAAmB,CACnB,eAAkB,CAClB,oDACE,eAAgB,CAChB,aAAgB,CAClB,gEACE,+CAAkD,CACpD,6FACE,YAAe,CACjB,kNACE,SAAY,CACd,sEACE,YAAa,CACb,iBAAoB,CACtB,sFACE,iFAAkF,CAClF,+EAAkF,CACtF,yTAIE,UAAW,CACX,SAAY,CACd,0CACE,iBAAkB,CAClB,cAAe,CACf,gBAAiB,CACjB,eAAkB,CAClB,gDACE,YAAe,CACnB,0CACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA+E,CACjF,+HAGE,gBAAiB,CACjB,mBAAsB,CACxB,yCACE,0DAA2D,CAC3D,4DAA+D,CAC/D,+DACE,kEAAqE,CACrE,mFACE,0EAA6E,CACjF,6DACE,6DAAgE,CACpE,4CACE,yDAA4D,CAC5D,gEACE,mEAAsE,CAC1E,0CACE,uDAAwD,CACxD,gBAAmB,CACnB,oCACE,0CACE,gBAAiB,CACjB,mBAAoB,CACpB,aAAgB,CAAE,CACxB,sDACE,aAAc,CACd,UAAa,CACf,+CACE,sDAAyD,CACzD,0EACE,qEAAwE,CAC5E,mCACE,iCAAoC,CACtC,wCACE,UAAW,CACX,kBAAqB,CACvB,qCACE,iCAAoC,CACtC,0CACE,8BAAiC,CAErC,oCACE,yBACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gDAAiD,CACjD,kDAAmD,CACnD,mDAAoD,CACpD,iDAAkD,CAClD,YAAa,CACb,WAAc,CACd,8BACE,UAAW,CACX,WAAY,CACZ,cAAe,CACf,gBAAiB,CACjB,kBAAmB,CACnB,kBAAqB,CACvB,2CACE,iBAAkB,CAClB,UAAW,CACX,WAAY,CACZ,cAAe,CACf,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CACpD,+BACE,YAAa,CACb,iBAAoB,CACtB,+BACE,aAAgB,CAChB,6CACE,oHAAuH,CAC3H,kDACE,YAAe,CACjB,6DACE,mHAAsH,CACxH,yGAEE,mFAAsF,CACxF,6CACE,wFAA2F,CAC3F,iFACE,eAAkB,CACpB,gEACE,uHAA0H,CAC9H,6DACE,YAAa,CACb,yBAA0B,CAC1B,WAAY,CACZ,6BAA8B,CAC9B,+DAAgE,CAChE,iMAAoM,CACpM,+DACE,6MAAgN,CAChN,0EACE,uGAA0G,CAChH,sCACE,+FAAgG,CAChG,qGAAsG,CACtG,qGAAsG,CACtG,2GAA4G,CAC5G,uCAA0C,CAC1C,0DACE,sEAAuE,CACvE,yEAA4E,CAC9E,uEACE,sFAAyF,CAC7F,6CACE,eAAkB,CACpB,sCACE,oGAAqG,CACrG,aAAc,CACd,+DAAgE,CAChE,yCAA2C,CAC3C,iBAAoB,CACpB,wCACE,aAAgB,CAClB,6CACE,eAAiB,CACjB,eAAgB,CAChB,wBAA2B,CAC/B,yCACE,iCAAoC,CACtC,wCACE,kCAAqC,CACvC,qCACE,kGAAmG,CACnG,sGAAuG,CACvG,wGAAyG,CACzG,oGAAqG,CACrG,QAAW,CACX,6GACE,yCAA4C,CAChD,gEACE,2EAA4E,CAC5E,0EAA2E,CAC3E,gEAAmE,CACrE,+BACE,iBAAoB,CACpB,qCACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,UAAW,CACX,QAAS,CACT,+GAAkH,CACpH,6CACE,+FAAkG,CAClG,mDACE,6CAAgD,CACpD,6EACE,sDAAuD,CACvD,eAAgB,CAChB,UAAW,CACX,4BAA+B,CACnC,qDACE,4CAA6C,CAC7C,8CAA+C,CAC/C,+CAAgD,CAChD,6CAA8C,CAC9C,kCAAmC,CACnC,iCAAkC,CAClC,aAAc,CACd,uDAAwD,CACxD,eAAgB,CAChB,kBAAmB,CACnB,eAAkB,CAClB,uDACE,eAAgB,CAChB,aAAgB,CAClB,mEACE,+CAAkD,CACpD,gGACE,YAAe,CACjB,wNACE,SAAY,CACd,yEACE,YAAa,CACb,iBAAoB,CACtB,yFACE,iFAAkF,CAClF,+EAAkF,CACtF,qUAIE,UAAW,CACX,SAAY,CACd,6CACE,iBAAkB,CAClB,cAAe,CACf,gBAAiB,CACjB,eAAkB,CAClB,mDACE,YAAe,CACnB,6CACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA+E,CACjF,wIAGE,gBAAiB,CACjB,mBAAsB,CACxB,4CACE,0DAA2D,CAC3D,4DAA+D,CAC/D,kEACE,kEAAqE,CACrE,sFACE,0EAA6E,CACjF,gEACE,6DAAgE,CACpE,+CACE,yDAA4D,CAC5D,mEACE,mEAAsE,CAC1E,6CACE,uDAAwD,CACxD,gBAAmB,CAAE,CACvB,0DACE,6CACE,gBAAiB,CACjB,mBAAoB,CACpB,aAAgB,CAAE,CAE1B,oCACI,yDACE,aAAc,CACd,UAAa,CACf,kDACE,sDAAyD,CACzD,6EACE,qEAAwE,CAC5E,sCACE,iCAAoC,CACtC,2CACE,UAAW,CACX,kBAAqB,CACvB,wCACE,iCAAoC,CACtC,6CACE,8BAAiC,CAAE,CAEzC,oCACE,yBACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gDAAiD,CACjD,kDAAmD,CACnD,mDAAoD,CACpD,iDAAkD,CAClD,YAAa,CACb,WAAc,CACd,8BACE,UAAW,CACX,WAAY,CACZ,cAAe,CACf,gBAAiB,CACjB,kBAAmB,CACnB,kBAAqB,CACvB,2CACE,iBAAkB,CAClB,UAAW,CACX,WAAY,CACZ,cAAe,CACf,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CACpD,+BACE,YAAa,CACb,iBAAoB,CACtB,+BACE,aAAgB,CAChB,6CACE,oHAAuH,CAC3H,kDACE,YAAe,CACjB,6DACE,mHAAsH,CACxH,yGAEE,mFAAsF,CACxF,6CACE,wFAA2F,CAC3F,iFACE,eAAkB,CACpB,gEACE,uHAA0H,CAC9H,6DACE,YAAa,CACb,yBAA0B,CAC1B,WAAY,CACZ,6BAA8B,CAC9B,+DAAgE,CAChE,iMAAoM,CACpM,+DACE,6MAAgN,CAChN,0EACE,uGAA0G,CAChH,sCACE,+FAAgG,CAChG,qGAAsG,CACtG,qGAAsG,CACtG,2GAA4G,CAC5G,uCAA0C,CAC1C,0DACE,sEAAuE,CACvE,yEAA4E,CAC9E,uEACE,sFAAyF,CAC7F,6CACE,eAAkB,CACpB,sCACE,oGAAqG,CACrG,aAAc,CACd,+DAAgE,CAChE,yCAA2C,CAC3C,iBAAoB,CACpB,wCACE,aAAgB,CAClB,6CACE,eAAiB,CACjB,eAAgB,CAChB,wBAA2B,CAC/B,yCACE,iCAAoC,CACtC,wCACE,kCAAqC,CACvC,qCACE,kGAAmG,CACnG,sGAAuG,CACvG,wGAAyG,CACzG,oGAAqG,CACrG,QAAW,CACX,6GACE,yCAA4C,CAChD,gEACE,2EAA4E,CAC5E,0EAA2E,CAC3E,gEAAmE,CACrE,+BACE,iBAAoB,CACpB,qCACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,UAAW,CACX,QAAS,CACT,+GAAkH,CACpH,6CACE,+FAAkG,CAClG,mDACE,6CAAgD,CACpD,6EACE,sDAAuD,CACvD,eAAgB,CAChB,UAAW,CACX,4BAA+B,CACnC,qDACE,4CAA6C,CAC7C,8CAA+C,CAC/C,+CAAgD,CAChD,6CAA8C,CAC9C,kCAAmC,CACnC,iCAAkC,CAClC,aAAc,CACd,uDAAwD,CACxD,eAAgB,CAChB,kBAAmB,CACnB,eAAkB,CAClB,uDACE,eAAgB,CAChB,aAAgB,CAClB,mEACE,+CAAkD,CACpD,gGACE,YAAe,CACjB,wNACE,SAAY,CACd,yEACE,YAAa,CACb,iBAAoB,CACtB,yFACE,iFAAkF,CAClF,+EAAkF,CACtF,qUAIE,UAAW,CACX,SAAY,CACd,6CACE,iBAAkB,CAClB,cAAe,CACf,gBAAiB,CACjB,eAAkB,CAClB,mDACE,YAAe,CACnB,6CACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA+E,CACjF,wIAGE,gBAAiB,CACjB,mBAAsB,CACxB,4CACE,0DAA2D,CAC3D,4DAA+D,CAC/D,kEACE,kEAAqE,CACrE,sFACE,0EAA6E,CACjF,gEACE,6DAAgE,CACpE,+CACE,yDAA4D,CAC5D,mEACE,mEAAsE,CAC1E,6CACE,uDAAwD,CACxD,gBAAmB,CAAE,CACvB,0DACE,6CACE,gBAAiB,CACjB,mBAAoB,CACpB,aAAgB,CAAE,CAE1B,oCACI,yDACE,aAAc,CACd,UAAa,CACf,kDACE,sDAAyD,CACzD,6EACE,qEAAwE,CAC5E,sCACE,iCAAoC,CACtC,2CACE,UAAW,CACX,kBAAqB,CACvB,wCACE,iCAAoC,CACtC,6CACE,8BAAiC,CAAE,CAEzC,qCACE,yBACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gDAAiD,CACjD,kDAAmD,CACnD,mDAAoD,CACpD,iDAAkD,CAClD,YAAa,CACb,WAAc,CACd,8BACE,UAAW,CACX,WAAY,CACZ,cAAe,CACf,gBAAiB,CACjB,kBAAmB,CACnB,kBAAqB,CACvB,2CACE,iBAAkB,CAClB,UAAW,CACX,WAAY,CACZ,cAAe,CACf,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CACpD,+BACE,YAAa,CACb,iBAAoB,CACtB,+BACE,aAAgB,CAChB,6CACE,oHAAuH,CAC3H,kDACE,YAAe,CACjB,6DACE,mHAAsH,CACxH,yGAEE,mFAAsF,CACxF,6CACE,wFAA2F,CAC3F,iFACE,eAAkB,CACpB,gEACE,uHAA0H,CAC9H,6DACE,YAAa,CACb,yBAA0B,CAC1B,WAAY,CACZ,6BAA8B,CAC9B,+DAAgE,CAChE,iMAAoM,CACpM,+DACE,6MAAgN,CAChN,0EACE,uGAA0G,CAChH,sCACE,+FAAgG,CAChG,qGAAsG,CACtG,qGAAsG,CACtG,2GAA4G,CAC5G,uCAA0C,CAC1C,0DACE,sEAAuE,CACvE,yEAA4E,CAC9E,uEACE,sFAAyF,CAC7F,6CACE,eAAkB,CACpB,sCACE,oGAAqG,CACrG,aAAc,CACd,+DAAgE,CAChE,yCAA2C,CAC3C,iBAAoB,CACpB,wCACE,aAAgB,CAClB,6CACE,eAAiB,CACjB,eAAgB,CAChB,wBAA2B,CAC/B,yCACE,iCAAoC,CACtC,wCACE,kCAAqC,CACvC,qCACE,kGAAmG,CACnG,sGAAuG,CACvG,wGAAyG,CACzG,oGAAqG,CACrG,QAAW,CACX,6GACE,yCAA4C,CAChD,gEACE,2EAA4E,CAC5E,0EAA2E,CAC3E,gEAAmE,CACrE,+BACE,iBAAoB,CACpB,qCACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,UAAW,CACX,QAAS,CACT,+GAAkH,CACpH,6CACE,+FAAkG,CAClG,mDACE,6CAAgD,CACpD,6EACE,sDAAuD,CACvD,eAAgB,CAChB,UAAW,CACX,4BAA+B,CACnC,qDACE,4CAA6C,CAC7C,8CAA+C,CAC/C,+CAAgD,CAChD,6CAA8C,CAC9C,kCAAmC,CACnC,iCAAkC,CAClC,aAAc,CACd,uDAAwD,CACxD,eAAgB,CAChB,kBAAmB,CACnB,eAAkB,CAClB,uDACE,eAAgB,CAChB,aAAgB,CAClB,mEACE,+CAAkD,CACpD,gGACE,YAAe,CACjB,wNACE,SAAY,CACd,yEACE,YAAa,CACb,iBAAoB,CACtB,yFACE,iFAAkF,CAClF,+EAAkF,CACtF,qUAIE,UAAW,CACX,SAAY,CACd,6CACE,iBAAkB,CAClB,cAAe,CACf,gBAAiB,CACjB,eAAkB,CAClB,mDACE,YAAe,CACnB,6CACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA+E,CACjF,wIAGE,gBAAiB,CACjB,mBAAsB,CACxB,4CACE,0DAA2D,CAC3D,4DAA+D,CAC/D,kEACE,kEAAqE,CACrE,sFACE,0EAA6E,CACjF,gEACE,6DAAgE,CACpE,+CACE,yDAA4D,CAC5D,mEACE,mEAAsE,CAC1E,6CACE,uDAAwD,CACxD,gBAAmB,CAAE,CACvB,2DACE,6CACE,gBAAiB,CACjB,mBAAoB,CACpB,aAAgB,CAAE,CAE1B,qCACI,yDACE,aAAc,CACd,UAAa,CACf,kDACE,sDAAyD,CACzD,6EACE,qEAAwE,CAC5E,sCACE,iCAAoC,CACtC,2CACE,UAAW,CACX,kBAAqB,CACvB,wCACE,iCAAoC,CACtC,6CACE,8BAAiC,CAAE,CAEzC,qCACE,0BACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,gDAAiD,CACjD,kDAAmD,CACnD,mDAAoD,CACpD,iDAAkD,CAClD,YAAa,CACb,WAAc,CACd,+BACE,UAAW,CACX,WAAY,CACZ,cAAe,CACf,gBAAiB,CACjB,kBAAmB,CACnB,kBAAqB,CACvB,4CACE,iBAAkB,CAClB,UAAW,CACX,WAAY,CACZ,cAAe,CACf,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CACpD,gCACE,YAAa,CACb,iBAAoB,CACtB,gCACE,aAAgB,CAChB,8CACE,oHAAuH,CAC3H,mDACE,YAAe,CACjB,8DACE,mHAAsH,CACxH,2GAEE,mFAAsF,CACxF,8CACE,wFAA2F,CAC3F,kFACE,eAAkB,CACpB,iEACE,uHAA0H,CAC9H,8DACE,YAAa,CACb,yBAA0B,CAC1B,WAAY,CACZ,6BAA8B,CAC9B,+DAAgE,CAChE,iMAAoM,CACpM,gEACE,6MAAgN,CAChN,2EACE,uGAA0G,CAChH,uCACE,+FAAgG,CAChG,qGAAsG,CACtG,qGAAsG,CACtG,2GAA4G,CAC5G,uCAA0C,CAC1C,2DACE,sEAAuE,CACvE,yEAA4E,CAC9E,wEACE,sFAAyF,CAC7F,8CACE,eAAkB,CACpB,uCACE,oGAAqG,CACrG,aAAc,CACd,+DAAgE,CAChE,yCAA2C,CAC3C,iBAAoB,CACpB,yCACE,aAAgB,CAClB,8CACE,eAAiB,CACjB,eAAgB,CAChB,wBAA2B,CAC/B,0CACE,iCAAoC,CACtC,yCACE,kCAAqC,CACvC,sCACE,kGAAmG,CACnG,sGAAuG,CACvG,wGAAyG,CACzG,oGAAqG,CACrG,QAAW,CACX,8GACE,yCAA4C,CAChD,iEACE,2EAA4E,CAC5E,0EAA2E,CAC3E,gEAAmE,CACrE,gCACE,iBAAoB,CACpB,sCACE,iBAAkB,CAClB,KAAM,CACN,QAAS,CACT,MAAO,CACP,UAAW,CACX,QAAS,CACT,+GAAkH,CACpH,8CACE,+FAAkG,CAClG,oDACE,6CAAgD,CACpD,8EACE,sDAAuD,CACvD,eAAgB,CAChB,UAAW,CACX,4BAA+B,CACnC,sDACE,4CAA6C,CAC7C,8CAA+C,CAC/C,+CAAgD,CAChD,6CAA8C,CAC9C,kCAAmC,CACnC,iCAAkC,CAClC,aAAc,CACd,uDAAwD,CACxD,eAAgB,CAChB,kBAAmB,CACnB,eAAkB,CAClB,wDACE,eAAgB,CAChB,aAAgB,CAClB,oEACE,+CAAkD,CACpD,iGACE,YAAe,CACjB,0NACE,SAAY,CACd,0EACE,YAAa,CACb,iBAAoB,CACtB,0FACE,iFAAkF,CAClF,+EAAkF,CACtF,yUAIE,UAAW,CACX,SAAY,CACd,8CACE,iBAAkB,CAClB,cAAe,CACf,gBAAiB,CACjB,eAAkB,CAClB,oDACE,YAAe,CACnB,8CACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA+E,CACjF,2IAGE,gBAAiB,CACjB,mBAAsB,CACxB,6CACE,0DAA2D,CAC3D,4DAA+D,CAC/D,mEACE,kEAAqE,CACrE,uFACE,0EAA6E,CACjF,iEACE,6DAAgE,CACpE,gDACE,yDAA4D,CAC5D,oEACE,mEAAsE,CAC1E,8CACE,uDAAwD,CACxD,gBAAmB,CAAE,CACvB,2DACE,8CACE,gBAAiB,CACjB,mBAAoB,CACpB,aAAgB,CAAE,CAE1B,qCACI,0DACE,aAAc,CACd,UAAa,CACf,mDACE,sDAAyD,CACzD,8EACE,qEAAwE,CAC5E,uCACE,iCAAoC,CACtC,4CACE,UAAW,CACX,kBAAqB,CACvB,yCACE,iCAAoC,CACtC,8CACE,8BAAiC,CAAE,CAEzC,YACE,oEAAqE,CACrE,4DAA6D,CAC7D,kEAAmE,CACnE,6DAA8D,CAC9D,wDAAyD,CACzD,6DAA8D,CAC9D,+DAAgE,CAChE,gEAAiE,CACjE,8DAA+D,CAC/D,mEAAoE,CACpE,kEAAmE,CACnE,kEAAmE,CACnE,wEAAyE,CACzE,kEAAmE,CACnE,qEAAsE,CACtE,2DAA4D,CAC5D,mEAAoE,CACpE,sDAAuD,CACvD,2DAA4D,CAC5D,6DAA8D,CAC9D,8DAA+D,CAC/D,4DAA6D,CAC7D,8EAA+E,CAC/E,+EAAgF,CAChF,kFAAmF,CACnF,mFAAoF,CACpF,8BAA+B,CAC/B,iCAAkC,CAClC,8BAA+B,CAC/B,oCAAqC,CACrC,qCAAsC,CACtC,qCAAsC,CACtC,oCAAqC,CACrC,yCAA0C,CAC1C,4CAA6C,CAC7C,iIAAkI,CAClI,sDAAuD,CACvD,mDAA8D,CAC9D,0DAA2D,CAC3D,sEAAuE,CACvE,sEAAuE,CACvE,iDAAkD,CAClD,wDAAyD,CACzD,+DAAgE,CAChE,+DAAgE,CAChE,gEAAiE,CACjE,8EAAiF,CACjF,6CAA8C,CAC9C,gDAAiD,CACjD,6CAA8C,CAC9C,mEAAoE,CACpE,wEAAyE,CACzE,0EAA2E,CAC3E,yEAA0E,CAC1E,2FAA8F,CAC9F,mFAAsF,CACtF,8FAAiG,CACjG,kFAAqF,CACrF,wFAAyF,CACzF,gFAAiF,CACjF,uFAAwF,CACxF,uFAAwF,CACxF,wFAAyF,CACzF,kCAAmC,CACnC,oCAAqC,CACrC,qCAAsC,CACtC,mCAAoC,CACpC,8CAA+C,CAC/C,gDAAiD,CACjD,iDAAkD,CAClD,+CAAgD,CAChD,qEAAsE,CACtE,iDAAkD,CAClD,6EAA8E,CAC9E,6EAA8E,CAC9E,gFAAiF,CACjF,uFAA0F,CAC1F,0FAA6F,CAC7F,yFAA0F,CAC1F,sDAAuD,CACvD,oFAAqF,CACrF,mEAAoE,CACpE,qKAAsK,CACtK,6EAA8E,CAC9E,qEAAsE,CACtE,gFAAiF,CACjF,oEAAqE,CACrE,0FAA6F,CAC7F,gGAAmG,CACnG,4FAA+F,CAC/F,8DAA+D,CAC/D,iFAAkF,CAClF,yCAA0C,CAC1C,oDAAqD,CACrD,2DAA4D,CAC5D,2DAA4D,CAC5D,4DAA6D,CAC7D,yEAA0E,CAC1E,qEAAsE,CACtE,yFAA0F,CAC1F,qFAAsF,CACtF,sFAAuF,CACvF,qFAAsF,CACtF,uCAAwC,CACxC,kEAAmE,CACnE,8CAA+C,CAC/C,8FAAiG,CACjG,iGAAoG,CACpG,8EAA+E,CAC/E,6EAA8E,CAC9E,0FAA2F,CAC3F,kGAAmG,CACnG,kGAAmG,CACnG,mGAAoG,CACpG,6GAA8G,CAC9G,uGAAwG,CACxG,2EAA4E,CAC5E,0EAA2E,CAC3E,yJAA4J,CAC5J,uJAA0J,CAC1J,4GAA6G,CAC7G,wGAAyG,CACzG,wEAAyE,CACzE,qJAAwJ,CACxJ,sJAAyJ,CACzJ,wGAAyG,CACzG,sEAAuE,CACvE,sEAAuE,CACvE,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,yFAA0F,CAC1F,0FAA2F,CAC3F,6FAA8F,CAC9F,8FAA+F,CAC/F,gEAAiE,CACjE,wFAAyF,CACzF,0FAA2F,CAC3F,2FAA4F,CAC5F,yFAA0F,CAC1F,kFAAmF,CACnF,iFAAkF,CAClF,8FAA+F,CAC/F,kCAAmC,CACnC,UAAW,CACX,mDAAsD,CACtD,qCACE,YACE,8EAA+E,CAC/E,4EAA+E,CAAE,CACrF,qCACE,YACE,8GAA+G,CAC/G,4GAA6G,CAC7G,kIAAmI,CACnI,oIAAuI,CAAE,CAC7I,uBACE,kBAAqB,CACvB,+BACE,iBAAoB,CACpB,wCACE,eAAkB,CAClB,0CACE,eAAgB,CAChB,KAAM,CACN,oCAAqC,CACrC,6CAAgD,CAChD,+CACE,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAC2F,CACnG,+FADQ,wFAEqF,CAC7F,iBACE,sFAAuF,CACvF,iBAAkB,CAClB,oCAAqC,CACrC,2CAA4C,CAC5C,2CAA4C,CAC5C,6JAA8J,CAC9J,0CAA2C,CAC3C,2CAA4C,CAC5C,+CAAgD,CAChD,oCAAqC,CACrC,mDAAoD,CACpD,6CAA8C,CAC9C,+CAAkD,CAClD,4BACE,sFAAyF,CAC3F,2BACE,wFAA2F,CAC7F,4BACE,iBAAoB,CACtB,sBACE,UAAW,CACX,WAAY,CACZ,SAAY,CACd,0BACE,sEAAyE,CAC3E,8BACE,sEAAuE,CACvE,wCAAyC,CACzC,wCAAyC,CACzC,oCAAuC,CAC3C,oBACE,iDAAkD,CAClD,uDAAwD,CACxD,mDAAoD,CACpD,6CAA8C,CAC9C,sCAAuC,CACvC,eAAgB,CAChB,mDAAsD,CACxD,kBACE,qEAAsE,CACtE,yEAA0E,CAC1E,qBAAwB,CAC1B,kBACE,yEAA0E,CAC1E,+EAAkF,CAClF,uBACE,wBAAyB,CACzB,uBAA0B,CAC5B,wCACE,iBAAkB,CAClB,iDAAkD,CAClD,uDAAwD,CACxD,MAAO,CACP,UAAW,CACX,4BAA6B,CAC7B,iIAAoI,CACtI,2CACE,qDAAsD,CACtD,kBAAqB,CACzB,gLACE,SAAY,CACd,8BACE,2CAA8C,CAChD,uBACE,iDAAoD,CACtD,6CAEE,0EAA2E,CAC3E,0EAA2E,CAC3E,mCAAoC,CACpC,yCAA0C,CAC1C,qCAAwC,CAC1C,uBAKE,qCAAwC,CAC1C,gDALE,8BAA+B,CAC/B,iCAAkC,CAClC,oCAAqC,CACrC,qCAOwC,CAL1C,yBAKE,qCAAwC,CAC1C,4DAEE,wCAAyC,CACzC,wCAAyC,CACzC,4BAA6B,CAC7B,oCAAqC,CACrC,qCAAsC,CACtC,qCAAwC,CAC1C,6BACE,wCAAyC,CACzC,qCAAwC,CAC1C,yCACE,eAAkB,CAClB,4DACE,aAAgB,CACpB,4HACE,YAAe,CACjB,6EACE,wFAA2F,CAC7F,0HACE,mBAAsB,CAE1B,kBACE,iCAAkC,CAClC,iBAAkB,CAClB,aAAc,CACd,oCAAqC,CACrC,2CAA4C,CAC5C,2CAA4C,CAC5C,0CAA2C,CAC3C,mDAAoD,CACpD,6CAA8C,CAC9C,+CAAkD,CAClD,gCACE,iCAAoC,CACpC,kCACE,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CAExD,oBACE,eAAgB,CAChB,UAAW,CACX,6JAA8J,CAC9J,iBAAkB,CAClB,mBAAoB,CACpB,sCAAuC,CACvC,eAAgB,CAChB,mBAAoB,CACpB,gBAAiB,CACjB,2DAA4D,CAC5D,QAAW,CACX,2BACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,cAAe,CACf,UAAa,CACf,0BACE,6CAAgD,CAClD,0BACE,6CAAgD,CAClD,2BACE,8CAAiD,CAErD,6FAEE,aAAc,CACd,UAAW,CACX,0CAA2C,CAC3C,mDAAoD,CACpD,+CAAkD,CAEpD,oCACE,8BAAiC,CAEnC,qDAEE,mBAAoB,CACpB,eAAgB,CAChB,qBAAsB,CACtB,sCAAyC,CACzC,yFAEE,cAAiB,CACnB,uUAQE,6CAAgD,CAClD,qWAQE,6CAAgD,CAClD,8oBAiBE,sCAAyC,CAE7C,4GAGE,mCAAsC,CAExC,0MAME,8BAA+B,CAC/B,4BAA+B,CAEjC,oBACE,kCAAmC,CACnC,iCAAkC,CAClC,kBAAqB,CACrB,iCACE,yDAA4D,CAC5D,wEACE,sFAAyF,CAC7F,6CACE,uEAAwE,CACxE,0EAA6E,CAC/E,wBACE,mBAAsB,CAE1B,mBACE,sEAAyE,CAE3E,mCACE,0EAA2E,CAC3E,uEAAwE,CACxE,gNAAmN,CACnN,kDACE,uFAA0F,CAE9F,oDAEE,gCAAiC,CACjC,wEAAyE,CACzE,mCAAoC,CACpC,sEAAuE,CACvE,aAAc,CACd,gBAAiB,CACjB,qBAAwB,CAE1B,gCACE,iCAAkC,CAClC,kCAAmC,CACnC,gBAAmB,CAErB,uCACE,uFAAwF,CACxF,qGAAsG,CACtG,qGAAsG,CACtG,uGAAwG,CACxG,iBAAoB,CACpB,qDACE,gBAAmB,CACrB,2DACE,cAAe,CACf,eAAkB,CAClB,oMACE,SAAY,CAChB,mIAEE,iBAAkB,CAClB,OAAQ,CACR,UAAW,CACX,kBAAmB,CACnB,cAAiB,CACnB,kEACE,KAAM,CACN,2EAA4E,CAC5E,uEAAwE,CACxE,sFAAuF,CACvF,iGAAkG,CAClG,+FAAkG,CACpG,iEACE,oEAAqE,CACrE,sEAAuE,CACvE,mBAAoB,CACpB,qFAAsF,CACtF,4FAA+F,CACjG,sJACE,0JAA2J,CAC3J,yJAA0J,CAC1J,sJAAyJ,CAC3J,mDACE,+DAAgE,CAChE,8DAAiE,CACnE,gFACE,uIAA0I,CAC5I,iEACE,0EAA6E,CAC/E,oDACE,uDAA0D,CAC1D,0CACE,oDACE,kBAAmB,CACnB,sCAAyC,CAAE,CAEnD,gCACE,sDAAuD,CACvD,gEAAmE,CACnE,6CACE,kFAAmF,CACnF,gFAAiF,CACjF,8DAA+D,CAC/D,oEAAqE,CACrE,iBAAkB,CAClB,aAAgB,CAEpB,sCACE,0EAA2E,CAC3E,8EAA+E,CAC/E,gFAAiF,CACjF,4EAA6E,CAC7E,YAAa,CACb,UAAW,CACX,qDAAsD,CACtD,2DAA4D,CAC5D,uDAA0D,CAC1D,4CACE,iGAAkG,CAClG,2FAA8F,CAChG,4CACE,iGAAkG,CAClG,2FAA8F,CAChG,6CACE,kGAAmG,CACnG,4FAA+F,CACjG,wDACE,kDAAqD,CAEzD,oDACE,8FAA+F,CAC/F,gGAAiG,CACjG,wDAA2D,CAE7D,4BACE,4EAA+E,CAEjF,gCACE,gGAAiG,CACjG,8GAA+G,CAC/G,8GAA+G,CAC/G,gHAAiH,CACjH,gEAAmE,CAErE,4BACE,aAAc,CACd,yDAA0D,CAC1D,8CAA+C,CAC/C,mBAAsB,CAExB,4BACE,gCAAiC,CACjC,mCAAoC,CACpC,iBAAkB,CAClB,iCAAkC,CAClC,8BAAiC,CACjC,6EAEE,wDAA2D,CAC7D,8FAEE,0EAA6E,CAC7E,sKAEE,SAAY,CAChB,gEACE,iEAAkE,CAClE,uEAA0E,CAC5E,0CACE,oFAAqF,CACrF,yDAA0D,CAC1D,mEAAsE,CACxE,gDACE,YAAa,CACb,iBAAoB,CAExB,wMAGE,iHAAoH,CAEtH,wCACE,wFAA2F,CAE7F,uCACE,0FAA6F,CAE/F,yBACE,mEAAoE,CACpE,6EAA8E,CAC9E,iFAAkF,CAClF,mFAAoF,CACpF,gFAAmF,CACnF,4BACE,+EAAgF,CAChF,iFAAoF,CACpF,6DACE,mEAAoE,CACpE,6EAA8E,CAC9E,mFAAsF,CACtF,0EACE,iGAAoG,CACtG,yEACE,mGAAsG,CAC5G,kCACE,0EAA2E,CAC3E,gFAAmF,CACrF,6CACE,oEAAqE,CACrE,0EAA2E,CAC3E,sEAAyE,CAC3E,6CACE,+EAAgF,CAChF,qFAAwF,CAC1F,2CACE,UAAW,CACX,WAAY,CACZ,iBAAoB,CACtB,qDACE,wFAA2F,CAC7F,oDACE,0FAA6F,CAC/F,6DACE,iHAAkH,CAClH,uHAA0H,CAE9H,yBACE,YAAa,CACb,kBAAqB,CACrB,2CACE,wDAA2D,CAE/D,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,2BACE,6BAAgC,CAElC,4BACE,8BAAiC,CAEnC,WACE,oBAAqB,CACrB,mEAAoE,CACpE,yEAA0E,CAC1E,qCAAsC,CACtC,uCAAwC,CACxC,mFAAoF,CACpF,sCAAuC,CACvC,2DAA4D,CAC5D,2CAA4C,CAC5C,kEAAmE,CACnE,kIAAmI,CACnI,4HAA6H,CAC7H,yEAA0E,CAC1E,2HAA4H,CAC5H,qDAAsD,CACtD,0DAA2D,CAC3D,8CAA+C,CAC/C,0CAAqD,CACrD,0DAA2D,CAC3D,4DAA6D,CAC7D,6DAA8D,CAC9D,2DAA4D,CAC5D,sEAAuE,CACvE,0FAA2F,CAC3F,sEAAuE,CACvE,yEAA0E,CAC1E,gFAAiF,CACjF,uEAAwE,CACxE,gFAAiF,CACjF,8FAA+F,CAC/F,+FAAgG,CAChG,+EAAgF,CAChF,2CAA4C,CAC5C,6CAA8C,CAC9C,8CAA+C,CAC/C,4CAA6C,CAC7C,2FAA8F,CAC9F,kCAAmC,CACnC,iCAAkC,CAClC,kCAAmC,CACnC,8EAA+E,CAC/E,uCAAwC,CACxC,0CAA2C,CAC3C,4CAA6C,CAC7C,2CAA4C,CAC5C,8EAA+E,CAC/E,8EAA+E,CAC/E,+EAAgF,CAChF,0FAA2F,CAC3F,wFAAyF,CACzF,kEAAmE,CACnE,8DAA+D,CAC/D,4EAA6E,CAC7E,iFAAkF,CAClF,kFAAmF,CACnF,+DAAgE,CAChE,mEAAoE,CACpE,+EAAkF,CAClF,4DAA6D,CAC7D,+DAAgE,CAChE,6DAA8D,CAC9D,qFAAsF,CACtF,wFAAyF,CACzF,sDAAuD,CACvD,iHAAkH,CAClH,qDAAsD,CACtD,iBAAkB,CAClB,YAAa,CACb,qCAAsC,CACtC,oCAAqC,CACrC,eAAkB,CAClB,qCACE,WACE,4EAA+E,CAAE,CACrF,kBACE,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,MAAO,CACP,kDAAmD,CACnD,sLAAyL,CAC3L,sCACE,eAAkB,CACpB,sCACE,WAAc,CACd,kDACE,iFAAoF,CACtF,iDACE,iFAAoF,CACxF,sCACE,eAAgB,CAChB,sBAAyB,CAC3B,qDACE,SAAY,CACd,mEACE,cAAe,CACf,uBAA0B,CAC5B,oEACE,aAAc,CACd,uBAA0B,CAC5B,2DACE,wCAA2C,CAC7C,+EACE,6CAAgD,CAClD,oBACE,iFAAkF,CAClF,+FAAgG,CAChG,8FAA+F,CAC/F,+BAAgC,CAChC,qCAAwC,CACxC,qCACE,mFAAsF,CACxF,gDACE,6CAAgD,CAClD,kDACE,2FAA4F,CAC5F,oFAAuF,CACzF,sFACE,sGAAyG,CAC3G,qFACE,uGAA0G,CAG5G,0LACE,gEAAqE,CACvE,mEACE,iCAAoC,CACtC,iDACE,qGAAsG,CACtG,uIAA0I,CAC9I,yBACE,sDAAuD,CACvD,wCAAyC,CACzC,iFAAkF,CAClF,4EAA6E,CAC7E,kFAAmF,CACnF,iCAAkC,CAClC,+BAAgC,CAChC,kCAAmC,CACnC,oCAAqC,CACrC,mBAAoB,CACpB,qBAAsB,CACtB,WAAY,CACZ,SAAY,CACZ,gCACE,KAAM,CACN,UAAa,CACf,0CACE,qBAAsB,CACtB,gDAAmD,CACrD,sDACE,kCAAqC,CACvC,qDACE,qCAAwC,CAC1C,0CACE,0CAA2C,CAC3C,oFAAqF,CACrF,cAAe,CACf,eAAkB,CACpB,+CACE,cAAe,CACf,wBAA2B,CAC/B,kCACE,6DAA8D,CAC9D,sCAAuC,CACvC,kFAAqF,CACrF,yCACE,OAAQ,CACR,SAAY,CACd,8DACE,8CAA+C,CAC/C,8FAAiG,CACnG,gEACE,oGAAqG,CACrG,+FAAgG,CAChG,+FAAkG,CAGpG,wJACE,4FAA+F,CACjG,yDACE,+DAAoE,CACtE,8LAEE,KAAQ,CACZ,0BACE,yEAA4E,CAEhF,iBACE,oBAAqB,CACrB,2CAA4C,CAC5C,iBAAkB,CAClB,YAAa,CACb,cAAe,CACf,eAAgB,CAChB,sBAAuB,CACvB,gCAAmC,CACnC,oCACE,YAAe,CAEnB,iBACE,YAAa,CACb,SAAY,CACZ,8BACE,uEAAwE,CACxE,iGAAkG,CAClG,iGAAoG,CAExG,2CAEE,QAAW,CAEb,kGAIE,iBAAkB,CAClB,OAAQ,CACR,QAAS,CACT,MAAO,CACP,UAAW,CACX,kBAAqB,CAEvB,gFAGE,KAAQ,CAEV,iBACE,sFAAuF,CACvF,iBAAkB,CAClB,YAAa,CACb,MAAO,CACP,yJAA0J,CAC1J,0CAA2C,CAC3C,mCAAoC,CACpC,oBAAqB,CACrB,wDAAyD,CACzD,oDAAuD,CACvD,wBACE,mBAAoB,CACpB,sEAAgE,CAAhE,qEAAgE,CAChE,8MAA+M,CAE/M,qNAAwE,CAC1E,uBACE,sCAAuC,CACvC,0CAA2C,CAC3C,4CAA6C,CAC7C,yCAA0C,CAC1C,uDAAwD,CACxD,0MAA6M,CAC/M,uBACE,uFAA0F,CAC5F,uBACE,uFAA0F,CAC5F,wBACE,wFAA2F,CAC7F,8EAEE,uDAA0D,CAC1D,oGAEE,uCAA0C,CAEhD,0BACE,SAAU,CACV,4CAA6C,CAC7C,aAAc,CACd,4CAA6C,CAC7C,iEAAkE,CAClE,6DAA8D,CAC9D,SAAU,CACV,2NAAgO,CAChO,iGACE,+EAAkF,CACpF,iCACE,iEAAkE,CAClE,0EAA6L,CAA7L,8EAA6L,CAA7L,4EAA6L,CAA7L,kBAA+L,CACjM,wCACE,gHAAiH,CACjH,4DAA+D,CAC/D,2BAA8B,CAChC,yCACE,+GAAgH,CAChH,2DAA8D,CAC9D,0BAA6B,CAC/B,mCACE,kFAAmF,CACnF,mBAAsB,CAE1B,2BACE,oBAAqB,CACrB,gCAAiC,CACjC,uCAA0C,CAE5C,yBACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CAEvE,yBACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CAEvE,yBACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CAEvE,yBACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CAEvE,0BACE,gDAAiD,CACjD,4DAA6D,CAC7D,mEAAsE,CAExE,yBACE,iCACE,oBAAqB,CACrB,gCAAiC,CACjC,uCAA0C,CAC5C,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,gDAAiD,CACjD,4DAA6D,CAC7D,mEAAsE,CAAE,CAE5E,yBACE,iCACE,oBAAqB,CACrB,gCAAiC,CACjC,uCAA0C,CAC5C,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,gDAAiD,CACjD,4DAA6D,CAC7D,mEAAsE,CAAE,CAE5E,yBACE,iCACE,oBAAqB,CACrB,gCAAiC,CACjC,uCAA0C,CAC5C,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,gDAAiD,CACjD,4DAA6D,CAC7D,mEAAsE,CAAE,CAE5E,0BACE,iCACE,oBAAqB,CACrB,gCAAiC,CACjC,uCAA0C,CAC5C,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,+BACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,gDAAiD,CACjD,4DAA6D,CAC7D,mEAAsE,CAAE,CAE5E,0BACE,kCACE,oBAAqB,CACrB,gCAAiC,CACjC,uCAA0C,CAC5C,gCACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,gCACE,+CAAgD,CAChD,2DAA4D,CAC5D,kEAAqE,CACvE,iCACE,gDAAiD,CACjD,4DAA6D,CAC7D,mEAAsE,CAAE,CAE5E,WACE,oDAAqD,CACrD,sDAAuD,CACvD,uDAAwD,CACxD,qDAAsD,CACtD,mEAAoE,CACpE,mEAAoE,CACpE,kEAAmE,CACnE,oEAAqE,CACrE,4EAA6E,CAC7E,8EAA+E,CAC/E,iFAAkF,CAClF,yEAA0E,CAC1E,4EAA6E,CAC7E,8EAA+E,CAC/E,sDAAuD,CACvD,qEAAsE,CACtE,0EAA2E,CAC3E,qEAAsE,CACtE,2EAA4E,CAC5E,2DAA4D,CAC5D,gEAAiE,CACjE,qDAAsD,CACtD,oEAAqE,CACrE,yEAA0E,CAC1E,0EAA2E,CAC3E,oEAAqE,CACrE,+EAAgF,CAChF,mFAAoF,CACpF,iGAAkG,CAClG,qDAAsD,CACtD,0DAA2D,CAC3D,0EAA2E,CAC3E,iBAAkB,CAClB,mBAAoB,CACpB,iIAAkI,CAClI,iBAAkB,CAClB,cAAe,CACf,kDAAmD,CACnD,8BAAiC,CACjC,kBACE,iBAAkB,CAClB,KAAM,CACN,OAAQ,CACR,QAAS,CACT,MAAO,CACP,mBAAoB,CACpB,UAAW,CACX,wFAAyF,CACzF,oDAAuD,CACzD,iBACE,+DAAgE,CAChE,6DAA8D,CAC9D,6EAAgF,CAClF,yBACE,oEAAqE,CACrE,kEAAmE,CACnE,kFAAmF,CACnF,kFAAqF,CACvF,iBACE,+DAAgE,CAChE,6DAA8D,CAC9D,6EAA8E,CAC9E,6EAAgF,CAClF,yBACE,0EAA2E,CAC3E,oEAAqE,CACrE,kEAAmE,CACnE,kCAAmC,CACnC,kEAAmE,CACnE,mBAAsB,CACxB,2DACE,6FAAgG,CAEpG,mBACE,YAAa,CACb,kBAAmB,CACnB,sBAAyB,CACzB,gCACE,gCAAiC,CACjC,+EAAgF,CAChF,qBAAsB,CACtB,sBAA0B,CAC1B,iDACE,YAAa,CACb,kBAAmB,CACnB,sBAAuB,CACvB,qEAAwE,CAE9E,kBACE,oCAAuC,CAEzC,iBACE,0CAA2C,CAC3C,mCAAsC,CAExC,iBACE,gDAAiD,CACjD,0CAA2C,CAC3C,mCAAsC,CAExC,YACE,0EAA2E,CAC3E,gEAAiE,CACjE,6DAA8D,CAC9D,oEAAqE,CACrE,gEAAiE,CACjE,6DAA8D,CAC9D,oEAAqE,CACrE,gEAAiE,CACjE,6DAA8D,CAC9D,oEAAqE,CACrE,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,+DAAgE,CAChE,2DAA4D,CAC5D,mEAAoE,CACpE,yCAA0C,CAC1C,qBAAwB,CACxB,qBACE,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAmD,CACrD,qBACE,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAmD,CACrD,qBACE,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAmD,CACrD,oBACE,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,oBACE,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CACpD,oBACE,2CAA4C,CAC5C,+CAAgD,CAChD,+CAAkD,CAEtD,gCACE,sEAAuE,CACvE,sEAAyE,CAE3E,mBACE,8EAA+E,CAC/E,sEAAuE,CACvE,iFAAkF,CAClF,qEAAsE,CACtE,oEAAqE,CACrE,6GAAgH,CAChH,+DAAgE,CAChE,mFAAoF,CACpF,yFAA0F,CAC1F,yFAA0F,CAC1F,4FAA6F,CAC7F,kFAAmF,CACnF,sGAAuG,CACvG,yGAA0G,CAC1G,sGAAuG,CACvG,yGAA0G,CAC1G,wEAAyE,CACzE,4FAA6F,CAC7F,6FAA8F,CAC9F,iFAAkF,CAClF,YAAe,CAEjB,gEACE,+FAAgG,CAChG,qGAAwG,CAE1G,+DACE,gGAAiG,CACjG,sGAAyG,CAE3G,2BACE,mBAAoB,CACpB,iMAAkM,CAClM,oDAAqD,CACrD,wDAAyD,CACzD,6CAA8C,CAC9C,kEAAmE,CACnE,QAAW,CACX,sCACE,uGAA0G,CAC5G,iCACE,qGAAsG,CACtG,oBAAuB,CACzB,iCACE,qGAAwG,CAC1G,yCACE,0GAA2G,CAC3G,sFAAyF,CAC3F,6EACE,wGAAyG,CACzG,oFAAqF,CACrF,mBAAsB,CAE1B,oGAEE,4DAA+D,CAEjE,cACE,iCAAkC,CAClC,yDAA0D,CAC1D,gEAAiE,CACjE,kEAAmE,CACnE,mEAAoE,CACpE,iEAAkE,CAClE,iEAAkE,CAClE,oFAAqF,CACrF,gEAAiE,CACjE,2DAA4D,CAC5D,4DAA6D,CAC7D,6CAA8C,CAC9C,4CAA6C,CAC7C,0CAA2C,CAC3C,+CAAgD,CAChD,+CAAgD,CAChD,4CAA6C,CAC7C,gDAAiD,CACjD,gDAAiD,CACjD,6CAA8C,CAC9C,6CAA8C,CAC9C,8CAA+C,CAC/C,2CAA4C,CAC5C,iBAAkB,CAClB,uCAAwC,CACxC,yCAA4C,CAC5C,4CACE,QAAS,CACT,QAAS,CACT,+KAAkL,CACpL,+CACE,KAAM,CACN,QAAS,CACT,wLAA2L,CAC7L,6CACE,OAAQ,CACR,OAAQ,CACR,kLAAqL,CACvL,8CACE,OAAQ,CACR,MAAO,CACP,qLAAwL,CAE5L,uBACE,iBAAkB,CAClB,iLAAkL,CAClL,gDAAiD,CACjD,yCAA0C,CAC1C,iBAAkB,CAClB,qBAAsB,CACtB,8DAAiE,CACjE,4CACE,eAAkB,CAEtB,qBACE,iBAAkB,CAClB,uCAAwC,CACxC,yCAA0C,CAC1C,mBAAoB,CACpB,8DAAiE,CAEnE,gBACE,8EAA+E,CAC/E,+DAAgE,CAChE,iFAAoF,CACpF,+CAAgD,CAChD,wJAA2J,CAC3J,mBAAoB,CACpB,kBAAqB,CACrB,mCACE,mBAAoB,CACpB,kDAAmD,CACnD,gBAAmB,CAEvB,gFAEE,kEAAqE,CAEvE,sBACE,+CAAkD,CAEpD,gBACE,yDAA0D,CAC1D,4DAA6D,CAC7D,6HAAgI,CAChI,0HAA2H,CAC3H,+DAAgE,CAChE,iEAAkE,CAClE,kEAAmE,CACnE,qCAAsC,CACtC,0DAA2D,CAC3D,sEAAuE,CACvE,gFAAiF,CACjF,qFAAsF,CACtF,mFAAoF,CACpF,gGAAiG,CACjG,kGAAmG,CACnG,qEAAsE,CACtE,2EAA4E,CAC5E,2EAA4E,CAC5E,uFAAwF,CACxF,+EAAgF,CAChF,0FAA2F,CAC3F,8EAA+E,CAC/E,+FAAkG,CAClG,kGAAqG,CACrG,sEAAuE,CACvE,qEAAsE,CACtE,oGAAqG,CACrG,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,sEAAuE,CACvE,uEAAwE,CACxE,uEAAwE,CACxE,wCAAyC,CACzC,iEAAkE,CAClE,uFAAwF,CACxF,oEAAqE,CACrE,0EAA2E,CAC3E,0EAA2E,CAC3E,6CAA8C,CAC9C,mDAAsD,CAExD,uJACE,wFAAyF,CACzF,iBAAoB,CAEtB,sBACE,iBAAkB,CAClB,YAAa,CACb,QAAS,CACT,kBAAmB,CACnB,WAAY,CACZ,6KAA8K,CAC9K,wCAAyC,CACzC,eAAgB,CAChB,cAAe,CACf,QAAW,CACX,mCACE,2EAA4E,CAC5E,8DAAiE,CACnE,4BACE,oEAAuE,CACzE,kDACE,yDAA4D,CAC5D,wEACE,yGAA4G,CAElH,kCACE,oBAAqB,CACrB,2DAA4D,CAC5D,8DAAiE,CAEnE,4BACE,2DAA8D,CAEhE,6BACE,mBAAoB,CACpB,kBAAmB,CACnB,sBAAuB,CACvB,qOAAsO,CACtO,+DAAgE,CAChE,qEAAsE,CACtE,QAAW,CAEb,mFACE,iBAAkB,CAClB,iEAAkE,CAClE,mEAAoE,CACpE,0FAA6F,CAE/F,2BACE,eAAgB,CAChB,sBAAuB,CACvB,kBAAqB,CAEvB,wBACE,qLAAwL,CAE1L,2BACE,4DAA6D,CAC7D,6CAAgD,CAElD,yBACE,YAAa,CACb,kBAAqB,CAEvB,qEAEE,oEAAuE,CAEzE,wBACE,qDAAsD,CACtD,0CAA2C,CAC3C,QAAW,CACX,8BACE,2EAA8E,CAChF,8BACE,2EAA4E,CAC5E,sEAAyE,CAE7E,sDACE,wIAA2I,CAC3I,kGAAqG,CAEvG,iFACE,wIAA2I,CAC3I,kGAAqG,CAEvG,4GACE,wIAA2I,CAC3I,kGAAqG,CAEvG,uIACE,wIAA2I,CAC3I,kGAAqG,CAEvG,kKACE,wIAA2I,CAC3I,kGAAqG,CAEvG,6LACE,wIAA2I,CAC3I,kGAAqG,CAEvG,wNACE,wIAA2I,CAC3I,kGAAqG,CAEvG,mPACE,wIAA2I,CAC3I,kGAAqG,CAEvG,8QACE,wIAA2I,CAC3I,kGAAqG,CAEvG,ySACE,yIAA4I,CAC5I,kGAAqG,CAEvG,aACE,0BAA2B,CAC3B,+CAAgD,CAChD,kFAAmF,CACnF,0DAA2D,CAC3D,8DAA+D,CAC/D,gEAAiE,CACjE,iEAAkE,CAClE,+DAAgE,CAChE,oEAAqE,CACrE,mEAAoE,CACpE,oEAAqE,CACrE,mEAAoE,CACpE,qGAAsG,CACtG,6BAA8B,CAC9B,4DAA6D,CAC7D,6DAA8D,CAC9D,gEAAiE,CACjE,mEAAoE,CACpE,oEAAqE,CACrE,2DAA4D,CAC5D,8EAA+E,CAC/E,mEAAoE,CACpE,mEAAoE,CACpE,uEAAwE,CACxE,iFAAkF,CAClF,4EAA6E,CAC7E,qGAAsG,CACtG,6CAA8C,CAC9C,8CAA+C,CAC/C,sCAAuC,CACvC,uFAAwF,CACxF,gFAAiF,CACjF,mEAAoE,CACpE,wEAAyE,CACzE,sFAAuF,CACvF,+FAAgG,CAChG,oFAAqF,CACrF,wEAAyE,CACzE,oFAAqF,CACrF,6EAA8E,CAC9E,0DAA2D,CAC3D,uEAAwE,CACxE,8DAA+D,CAC/D,gEAAiE,CACjE,iEAAkE,CAClE,kJAAmJ,CACnJ,sFAAuF,CACvF,uFAAwF,CACxF,iKAAkK,CAClK,wCAAyC,CACzC,yFAA0F,CAC1F,0EAA2E,CAC3E,oEAAqE,CACrE,oGAAuG,CACvG,wEAAyE,CACzE,yEAA0E,CAC1E,uEAAwE,CACxE,6DAA8D,CAC9D,uDAAwD,CACxD,0EAA2E,CAC3E,oEAAqE,CACrE,8BAA+B,CAC/B,uCAAwC,CACxC,0EAA2E,CAC3E,2EAA4E,CAC5E,gEAAiE,CACjE,kEAAmE,CACnE,mEAAoE,CACpE,oJAAqJ,CACrJ,oEAAqE,CACrE,sEAAuE,CACvE,uEAAwE,CACxE,oEAAqE,CACrE,sEAAuE,CACvE,uEAAwE,CACxE,wJAAyJ,CACzJ,wEAAyE,CACzE,uEAAwE,CACxE,+DAAgE,CAChE,iFAAkF,CAClF,yEAA0E,CAC1E,wDAAyD,CACzD,iEAAkE,CAClE,mEAAoE,CACpE,oEAAqE,CACrE,kEAAmE,CACnE,qEAAsE,CACtE,uEAAwE,CACxE,wEAAyE,CACzE,sEAAuE,CACvE,8DAA+D,CAC/D,gEAAiE,CACjE,iEAAkE,CAClE,+DAAgE,CAChE,kEAAmE,CACnE,oEAAqE,CACrE,qEAAsE,CACtE,mEAAoE,CACpE,sEAAuE,CACvE,uEAAwE,CACxE,iBAAkB,CAClB,YAAa,CACb,qBAAsB,CACtB,iCAAoC,CACpC,oCACE,aACE,gFAAiF,CACjF,8EAAiF,CAAE,CACvF,qCACE,aACE,gFAAiF,CACjF,8EAA+E,CAG/E,gEAHiF,CAAE,CAIvF,oCACE,aACE,4DAA6D,CAC7D,kCAAmC,CAGnC,gFAAiF,CACjF,oFAAqF,CACrF,sFALqC,CAAE,CAM3C,qCACE,aACE,gFAAiF,CACjF,oFAAqF,CACrF,sFAAuF,CACvF,kFAAmF,CAGnF,kFAAmF,CACnF,sFAAuF,CACvF,wFAAyF,CACzF,oFAAqF,CAGrF,4EAA6E,CAC7E,gFAAiF,CACjF,kFAAmF,CACnF,8EAZqF,CAAE,CAa3F,6BACE,mDAAoD,CACpD,YAAe,CACjB,4CACE,aAAgB,CAClB,2BACE,4CAA+C,CAC/C,6IAGE,YAAa,CACb,iBAAoB,CAE1B,qBACE,kCAAmC,CACnC,iBAAkB,CAClB,0CAA2C,CAC3C,yKAA0K,CAC1K,4DAA+D,CAC/D,yCACE,iBAAkB,CAClB,kCAAmC,CACnC,sCAAuC,CACvC,6CAAgD,CAEpD,oBACE,qDAAsD,CACtD,oBAAuB,CAEzB,0BACE,YAAa,CACb,uDAAwD,CACxD,4CAA6C,CAC7C,iBAAoB,CACpB,oCACE,0BACE,aAAc,CACd,kBAAqB,CAAE,CAE7B,qBACE,iBAAkB,CAClB,0CAA2C,CAC3C,YAAa,CACb,6BAA8B,CAC9B,UAAW,CACX,yKAA0K,CAC1K,4DAA6D,CAC7D,QAAS,CACT,gDAAmD,CACnD,oCACE,qBACE,YAAa,CACb,iBAAoB,CAAE,CAC1B,mCACE,qCAAsC,CACtC,uIAA0I,CAC1I,6DACE,6EAAgF,CAEtF,0BACE,iBAAkB,CAClB,YAAa,CACb,cAAe,CACf,oBAAqB,CACrB,yDAA0D,CAC1D,2DAA4D,CAC5D,eAAkB,CAEpB,+BACE,gEAAiE,CACjE,eAAgB,CAChB,qBAAwB,CACxB,gDACE,8EAAiF,CAErF,yBACE,gFAAmF,CAErF,+BACE,4DAA6D,CAC7D,iDAAoD,CAEtD,0BACE,uDAA0D,CAE5D,yBACE,iBAAkB,CAClB,YAAa,CACb,qBAAsB,CACtB,WAAY,CACZ,YAAa,CACb,gEAAmE,CACnE,oCACE,yBACE,4DAA+D,CAAE,CAEvE,yBACE,iBAAkB,CAClB,YAAa,CACb,qBAAsB,CACtB,WAAY,CACZ,YAAe,CACf,oCACE,yBACE,eAAkB,CAAE,CAE1B,kBACE,iBAAkB,CAClB,KAAM,CACN,MAAO,CACP,uCAAwC,CACxC,YAAa,CACb,oCAAqC,CACrC,eAAgB,CAChB,eAAgB,CAChB,gCAAiC,CACjC,iBAAkB,CAClB,yDAA0D,CAC1D,6CAAgD,CAChD,gCACE,aAAc,CACd,kBAAqB,CACvB,oCACE,kBACE,aAAc,CACd,WAAY,CACZ,kBAAmB,CACnB,8GAAiH,CAAE,CAEzH,uBAIE,iLAAuD,CACvD,eAAgB,CAChB,8BAAiC,CACjC,8CACE,SAAU,CACV,0DAA2D,CAC3D,4DAA+D,CAC/D,4EACE,YAAe,CACjB,kFACE,mFAAsF,CAE5F,8CACE,kDAAqD,CAEvD,uBACE,iBAAkB,CAClB,oBAAqB,CACrB,yCAA0C,CAC1C,eAAgB,CAChB,4DAA6D,CAC7D,qBAAsB,CACtB,QAAW,CACX,uDACE,iBAAkB,CAClB,6CAA8C,CAC9C,MAAO,CACP,mBAAoB,CACpB,kBAAmB,CACnB,sBAAuB,CACvB,iDAAkD,CAClD,mDAAoD,CACpD,wDAAyD,CACzD,aAAc,CACd,iDAAkD,CAClD,sEAAuE,CACvE,gEAAiE,CACjE,sEAAyE,CAC3E,8BACE,KAAM,CACN,iCAAkC,CAClC,kCAAqC,CACvC,6BACE,yEAA4E,CAC9E,6BACE,yEAA4E,CAC9E,oCACE,6EAA8E,CAC9E,+DAAkE,CAClE,oEACE,iHAAkH,CAClH,6FAAgG,CACpG,qEACE,8EAA+E,CAC/E,mBAAsB,CACtB,mFACE,kHAAmH,CACnH,8FAAiG,CAEvG,mBACE,wCAAyC,CACzC,aAAc,CACd,iBAAkB,CAClB,eAAgB,CAChB,qBAAwB,CAE1B,wBACE,qLAAwL,CACxL,wCACE,SAAY,CAEhB,qBACE,YAAa,CACb,cAAe,CACf,aAAc,CACd,yKAA4K,CAC5K,uBACE,6DAAgE,CAChE,uCACE,2DAA8D,CAEpE,eACE,0BAA2B,CAC3B,YAAa,CACb,kBAAmB,CACnB,sBAAuB,CACvB,WAAY,CACZ,qCAAsC,CACtC,QAAW,CAEb,WACE,yBAA0B,CAC1B,0BAA2B,CAC3B,gCAAiC,CACjC,uCAAwC,CACxC,+CAAgD,CAChD,0BAA2B,CAC3B,qDAAsD,CACtD,iDAAkD,CAClD,2BAA4B,CAC5B,oDAAqD,CACrD,oDAAqD,CACrD,oDAAqD,CACrD,oDAAqD,CACrD,oDAAqD,CACrD,sDAAuD,CACvD,sDAAuD,CACvD,sDAAuD,CACvD,iCAAkC,CAClC,oCAAqC,CACrC,wCAA2C,CAC3C,sBACE,qBAAwB,CAE5B,aACE,iDAAkD,CAClD,mCAAoC,CACpC,cAAe,CACf,qCAAwC,CACxC,oCACE,aACE,uEAA2E,CAAE,CACjF,oCACE,aACE,2GAAgH,CAAE,CACtH,oCACE,aACE,+IAAqJ,CAAE,CAC3J,qCACE,aACE,mLAA0L,CAAE,CAChM,qCACE,aACE,wNAAgO,CAAE,CACtO,uBACE,qBAAwB,CAE5B,qBACE,iCAAoC,CAEtC,4BACE,gCAAmC,CAErC,uBACE,qBAAsB,CACtB,kBAAqB,CACrB,yBACE,qCAAwC,CAE5C,+BACE,6BAA8B,CAC9B,kBAAqB,CACrB,iCACE,qCAAwC,CAE5C,oBACE,kBAAmB,CACnB,+CAAkD,CAClD,sBACE,qCAAwC,CAE5C,4BACE,0BAA2B,CAC3B,uDAA0D,CAC1D,8BACE,qCAAwC,CAE5C,qBACE,cAAiB,CAEnB,6BACE,sBAAyB,CAE3B,uBACE,gBAAmB,CAErB,2CACE,0BAA6B,CAE/B,yCACE,wBAA2B,CAE7B,uCACE,sBAAyB,CAE3B,8CACE,6BAAgC,CAElC,6CACE,4BAA+B,CAEjC,6CACE,4BAA+B,CAEjC,uCACE,sBAAyB,CAE3B,qCACE,oBAAuB,CAEzB,mCACE,kBAAqB,CAEvB,oCACE,mBAAsB,CAExB,qCACE,oBAAuB,CAEzB,yCACE,wBAA2B,CAE7B,uCACE,sBAAyB,CAE3B,qCACE,oBAAuB,CAEzB,sCACE,qBAAwB,CAE1B,4CACE,2BAA8B,CAEhC,2CACE,0BAA6B,CAE/B,6BACE,gBAAmB,CAErB,4BACE,aAAgB,CAElB,sBACE,WAAc,CAEhB,wBACE,aAAgB,CAElB,4BACE,UAAW,CACX,cAAiB,CAEnB,wBACE,UAAa,CAEf,wBACE,UAAa,CAEf,wBACE,UAAa,CAEf,wBACE,UAAa,CAEf,8BACE,aAAgB,CAElB,2BACE,SAAY,CAEd,uCACE,qBAAwB,CAE1B,qCACE,mBAAsB,CAExB,mCACE,iBAAoB,CAEtB,qCACE,mBAAsB,CAExB,oCACE,kBAAqB,CAEvB,yBACE,2BACE,iCAAoC,CACtC,kCACE,gCAAmC,CACrC,6BACE,qBAAsB,CACtB,kBAAqB,CACrB,+BACE,qCAAwC,CAC5C,qCACE,6BAA8B,CAC9B,kBAAqB,CACrB,uCACE,qCAAwC,CAC5C,0BACE,kBAAmB,CACnB,+CAAkD,CAClD,4BACE,qCAAwC,CAC5C,kCACE,0BAA2B,CAC3B,uDAA0D,CAC1D,oCACE,qCAAwC,CAC5C,2BACE,cAAiB,CACnB,mCACE,sBAAyB,CAC3B,6BACE,gBAAmB,CACrB,iDACE,0BAA6B,CAC/B,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,oDACE,6BAAgC,CAClC,mDACE,4BAA+B,CACjC,mDACE,4BAA+B,CACjC,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,yCACE,kBAAqB,CACvB,0CACE,mBAAsB,CACxB,2CACE,oBAAuB,CACzB,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,4CACE,qBAAwB,CAC1B,kDACE,2BAA8B,CAChC,iDACE,0BAA6B,CAC/B,mCACE,gBAAmB,CACrB,kCACE,aAAgB,CAClB,4BACE,WAAc,CAChB,8BACE,aAAgB,CAClB,kCACE,UAAW,CACX,cAAiB,CACnB,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,oCACE,aAAgB,CAClB,iCACE,SAAY,CACd,6CACE,qBAAwB,CAC1B,2CACE,mBAAsB,CACxB,yCACE,iBAAoB,CACtB,2CACE,mBAAsB,CACxB,0CACE,kBAAqB,CAAE,CAE3B,yBACE,2BACE,iCAAoC,CACtC,kCACE,gCAAmC,CACrC,6BACE,qBAAsB,CACtB,kBAAqB,CACrB,+BACE,qCAAwC,CAC5C,qCACE,6BAA8B,CAC9B,kBAAqB,CACrB,uCACE,qCAAwC,CAC5C,0BACE,kBAAmB,CACnB,+CAAkD,CAClD,4BACE,qCAAwC,CAC5C,kCACE,0BAA2B,CAC3B,uDAA0D,CAC1D,oCACE,qCAAwC,CAC5C,2BACE,cAAiB,CACnB,mCACE,sBAAyB,CAC3B,6BACE,gBAAmB,CACrB,iDACE,0BAA6B,CAC/B,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,oDACE,6BAAgC,CAClC,mDACE,4BAA+B,CACjC,mDACE,4BAA+B,CACjC,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,yCACE,kBAAqB,CACvB,0CACE,mBAAsB,CACxB,2CACE,oBAAuB,CACzB,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,4CACE,qBAAwB,CAC1B,kDACE,2BAA8B,CAChC,iDACE,0BAA6B,CAC/B,mCACE,gBAAmB,CACrB,kCACE,aAAgB,CAClB,4BACE,WAAc,CAChB,8BACE,aAAgB,CAClB,kCACE,UAAW,CACX,cAAiB,CACnB,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,oCACE,aAAgB,CAClB,iCACE,SAAY,CACd,6CACE,qBAAwB,CAC1B,2CACE,mBAAsB,CACxB,yCACE,iBAAoB,CACtB,2CACE,mBAAsB,CACxB,0CACE,kBAAqB,CAAE,CAE3B,yBACE,2BACE,iCAAoC,CACtC,kCACE,gCAAmC,CACrC,6BACE,qBAAsB,CACtB,kBAAqB,CACrB,+BACE,qCAAwC,CAC5C,qCACE,6BAA8B,CAC9B,kBAAqB,CACrB,uCACE,qCAAwC,CAC5C,0BACE,kBAAmB,CACnB,+CAAkD,CAClD,4BACE,qCAAwC,CAC5C,kCACE,0BAA2B,CAC3B,uDAA0D,CAC1D,oCACE,qCAAwC,CAC5C,2BACE,cAAiB,CACnB,mCACE,sBAAyB,CAC3B,6BACE,gBAAmB,CACrB,iDACE,0BAA6B,CAC/B,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,oDACE,6BAAgC,CAClC,mDACE,4BAA+B,CACjC,mDACE,4BAA+B,CACjC,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,yCACE,kBAAqB,CACvB,0CACE,mBAAsB,CACxB,2CACE,oBAAuB,CACzB,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,4CACE,qBAAwB,CAC1B,kDACE,2BAA8B,CAChC,iDACE,0BAA6B,CAC/B,mCACE,gBAAmB,CACrB,kCACE,aAAgB,CAClB,4BACE,WAAc,CAChB,8BACE,aAAgB,CAClB,kCACE,UAAW,CACX,cAAiB,CACnB,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,oCACE,aAAgB,CAClB,iCACE,SAAY,CACd,6CACE,qBAAwB,CAC1B,2CACE,mBAAsB,CACxB,yCACE,iBAAoB,CACtB,2CACE,mBAAsB,CACxB,0CACE,kBAAqB,CAAE,CAE3B,0BACE,2BACE,iCAAoC,CACtC,kCACE,gCAAmC,CACrC,6BACE,qBAAsB,CACtB,kBAAqB,CACrB,+BACE,qCAAwC,CAC5C,qCACE,6BAA8B,CAC9B,kBAAqB,CACrB,uCACE,qCAAwC,CAC5C,0BACE,kBAAmB,CACnB,+CAAkD,CAClD,4BACE,qCAAwC,CAC5C,kCACE,0BAA2B,CAC3B,uDAA0D,CAC1D,oCACE,qCAAwC,CAC5C,2BACE,cAAiB,CACnB,mCACE,sBAAyB,CAC3B,6BACE,gBAAmB,CACrB,iDACE,0BAA6B,CAC/B,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,oDACE,6BAAgC,CAClC,mDACE,4BAA+B,CACjC,mDACE,4BAA+B,CACjC,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,yCACE,kBAAqB,CACvB,0CACE,mBAAsB,CACxB,2CACE,oBAAuB,CACzB,+CACE,wBAA2B,CAC7B,6CACE,sBAAyB,CAC3B,2CACE,oBAAuB,CACzB,4CACE,qBAAwB,CAC1B,kDACE,2BAA8B,CAChC,iDACE,0BAA6B,CAC/B,mCACE,gBAAmB,CACrB,kCACE,aAAgB,CAClB,4BACE,WAAc,CAChB,8BACE,aAAgB,CAClB,kCACE,UAAW,CACX,cAAiB,CACnB,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,8BACE,UAAa,CACf,oCACE,aAAgB,CAClB,iCACE,SAAY,CACd,6CACE,qBAAwB,CAC1B,2CACE,mBAAsB,CACxB,yCACE,iBAAoB,CACtB,2CACE,mBAAsB,CACxB,0CACE,kBAAqB,CAAE,CAE3B,0BACE,4BACE,iCAAoC,CACtC,mCACE,gCAAmC,CACrC,8BACE,qBAAsB,CACtB,kBAAqB,CACrB,gCACE,qCAAwC,CAC5C,sCACE,6BAA8B,CAC9B,kBAAqB,CACrB,wCACE,qCAAwC,CAC5C,2BACE,kBAAmB,CACnB,+CAAkD,CAClD,6BACE,qCAAwC,CAC5C,mCACE,0BAA2B,CAC3B,uDAA0D,CAC1D,qCACE,qCAAwC,CAC5C,4BACE,cAAiB,CACnB,oCACE,sBAAyB,CAC3B,8BACE,gBAAmB,CACrB,kDACE,0BAA6B,CAC/B,gDACE,wBAA2B,CAC7B,8CACE,sBAAyB,CAC3B,qDACE,6BAAgC,CAClC,oDACE,4BAA+B,CACjC,oDACE,4BAA+B,CACjC,8CACE,sBAAyB,CAC3B,4CACE,oBAAuB,CACzB,0CACE,kBAAqB,CACvB,2CACE,mBAAsB,CACxB,4CACE,oBAAuB,CACzB,gDACE,wBAA2B,CAC7B,8CACE,sBAAyB,CAC3B,4CACE,oBAAuB,CACzB,6CACE,qBAAwB,CAC1B,mDACE,2BAA8B,CAChC,kDACE,0BAA6B,CAC/B,oCACE,gBAAmB,CACrB,mCACE,aAAgB,CAClB,6BACE,WAAc,CAChB,+BACE,aAAgB,CAClB,mCACE,UAAW,CACX,cAAiB,CACnB,+BACE,UAAa,CACf,+BACE,UAAa,CACf,+BACE,UAAa,CACf,+BACE,UAAa,CACf,qCACE,aAAgB,CAClB,kCACE,SAAY,CACd,8CACE,qBAAwB,CAC1B,4CACE,mBAAsB,CACxB,0CACE,iBAAoB,CACtB,4CACE,mBAAsB,CACxB,2CACE,kBAAqB,CAAE,CAE3B,mCACE,kDAAqD,CAEvD,6CACE,qBAAwB,CAE1B,iCACE,gDAAmD,CAErD,2CACE,qBAAwB,CAE1B,iCACE,gDAAmD,CAErD,2CACE,qBAAwB,CAE1B,iCACE,gDAAmD,CAErD,2CACE,qBAAwB,CAE1B,iCACE,gDAAmD,CAErD,2CACE,qBAAwB,CAE1B,iCACE,gDAAmD,CAErD,2CACE,qBAAwB,CAE1B,kCACE,iDAAoD,CAEtD,4CACE,qBAAwB,CAE1B,kCACE,iDAAoD,CAEtD,4CACE,qBAAwB,CAE1B,kCACE,iDAAoD,CAEtD,4CACE,qBAAwB,CAE1B,yBACE,yCACE,kDAAqD,CACvD,mDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAAE,CAE9B,yBACE,yCACE,kDAAqD,CACvD,mDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAAE,CAE9B,yBACE,yCACE,kDAAqD,CACvD,mDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAAE,CAE9B,0BACE,yCACE,kDAAqD,CACvD,mDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,uCACE,gDAAmD,CACrD,iDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAC1B,wCACE,iDAAoD,CACtD,kDACE,qBAAwB,CAAE,CAE9B,0BACE,0CACE,kDAAqD,CACvD,oDACE,qBAAwB,CAC1B,wCACE,gDAAmD,CACrD,kDACE,qBAAwB,CAC1B,wCACE,gDAAmD,CACrD,kDACE,qBAAwB,CAC1B,wCACE,gDAAmD,CACrD,kDACE,qBAAwB,CAC1B,wCACE,gDAAmD,CACrD,kDACE,qBAAwB,CAC1B,wCACE,gDAAmD,CACrD,kDACE,qBAAwB,CAC1B,yCACE,iDAAoD,CACtD,mDACE,qBAAwB,CAC1B,yCACE,iDAAoD,CACtD,mDACE,qBAAwB,CAC1B,yCACE,iDAAoD,CACtD,mDACE,qBAAwB,CAAE,CAI5B,qEACE,kDAAqD,CAIvD,iEACE,gDAAmD,CAIrD,iEACE,gDAAmD,CAIrD,iEACE,gDAAmD,CAIrD,iEACE,gDAAmD,CAIrD,iEACE,gDAAmD,CAIrD,mEACE,iDAAoD,CAItD,mEACE,iDAAoD,CAItD,mEACE,iDAAoD,CAExD,yBAGI,iFACE,kDAAqD,CAGvD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAAE,CAE5D,yBAGI,iFACE,kDAAqD,CAGvD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAAE,CAE5D,yBAGI,iFACE,kDAAqD,CAGvD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAAE,CAE5D,0BAGI,iFACE,kDAAqD,CAGvD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,6EACE,gDAAmD,CAGrD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAGtD,+EACE,iDAAoD,CAAE,CAE5D,0BAGI,mFACE,kDAAqD,CAGvD,+EACE,gDAAmD,CAGrD,+EACE,gDAAmD,CAGrD,+EACE,gDAAmD,CAGrD,+EACE,gDAAmD,CAGrD,+EACE,gDAAmD,CAGrD,iFACE,iDAAoD,CAGtD,iFACE,iDAAoD,CAGtD,iFACE,iDAAoD,CAAE,CAE5D,cACE,0DAA2D,CAC3D,8CAA+C,CAE/C,4CAA6C,CAE7C,yKAA4K,CAC5K,qCAAsC,CACtC,YAAa,CACb,8DAA+D,CAC/D,wDAAyD,CACzD,8FAA+F,CAC/F,8FAAiG,CACjG,0BACE,+CAAkD,CACpD,yBACE,cACE,kJAAsJ,CAAE,CAC5J,yBACE,cACE,sMAA2M,CAAE,CACjN,yBACE,cACE,0PAAgQ,CAAE,CACtQ,0BACE,cACE,8SAAqT,CAAE,CAC3T,0BACE,cACE,mWAA2W,CAAE,CACjX,yBACE,cACE,kJAAsJ,CAAE,CAC5J,yBACE,cACE,sMAA2M,CAAE,CACjN,yBACE,cACE,0PAAgQ,CAAE,CACtQ,0BACE,cACE,8SAAqT,CAAE,CAC3T,0BACE,cACE,mWAA2W,CAAE,CAEnX,WACE,uDAAwD,CACxD,uCAAwC,CACxC,wCAAyC,CACzC,0BAA2B,CAC3B,YAAa,CACb,gDAAoD,CACpD,yCAEE,WAAY,CACZ,YAAa,CACb,yDAA0D,CAC1D,qDAAsD,CACtD,mCAAsC,CACtC,yBACE,yCAEE,uEAA2E,CAAE,CACjF,yBACE,yCAEE,2GAAgH,CAAE,CACtH,yBACE,yCAEE,+IAAqJ,CAAE,CAC3J,0BACE,yCAEE,mLAA0L,CAAE,CAChM,0BACE,yCAEE,wNAAgO,CAAE,CACxO,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,4BACE,uCAA0C,CAC5C,6BACE,wCAA2C,CAC7C,6BACE,wCAA2C,CAC7C,6BACE,wCAA2C,CAC7C,oCACE,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAAE,CACjD,oCACE,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAAE,CACjD,oCACE,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAAE,CACjD,qCACE,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,kCACE,uCAA0C,CAC5C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAC7C,mCACE,wCAA2C,CAAE,CACjD,qCACE,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,mCACE,uCAA0C,CAC5C,oCACE,wCAA2C,CAC7C,oCACE,wCAA2C,CAC7C,oCACE,wCAA2C,CAAE,CACjD,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,uBACE,uCAA0C,CAC5C,wBACE,wCAA2C,CAC7C,wBACE,wCAA2C,CAC7C,wBACE,wCAA2C,CAC7C,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,8CAA2D,CAC7D,8BACE,+CAA2D,CAC7D,+BACE,+CAA4D,CAC9D,+BACE,+CAA4D,CAC9D,+BACE,+CAA4D,CAC9D,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,uBACE,eAAkB,CACpB,wBACE,gBAAmB,CACrB,wBACE,gBAAmB,CACrB,wBACE,gBAAmB,CACrB,oCACE,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,+CAA2D,CAC7D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CAAE,CACzB,oCACE,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,+CAA2D,CAC7D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CAAE,CACzB,oCACE,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,+CAA2D,CAC7D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CAAE,CACzB,qCACE,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,6BACE,uCAA0C,CAC5C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,8BACE,wCAA2C,CAC7C,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,8CAA2D,CAC7D,oCACE,+CAA2D,CAC7D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,qCACE,+CAA4D,CAC9D,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,6BACE,eAAkB,CACpB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CACrB,8BACE,gBAAmB,CAAE,CACzB,qCACE,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,8BACE,uCAA0C,CAC5C,+BACE,wCAA2C,CAC7C,+BACE,wCAA2C,CAC7C,+BACE,wCAA2C,CAC7C,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,8CAA2D,CAC7D,qCACE,+CAA2D,CAC7D,sCACE,+CAA4D,CAC9D,sCACE,+CAA4D,CAC9D,sCACE,+CAA4D,CAC9D,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,8BACE,eAAkB,CACpB,+BACE,gBAAmB,CACrB,+BACE,gBAAmB,CACrB,+BACE,gBAAmB,CAAE,CACzB,uBACE,4CAA+C,CAEnD,YACE,4DAA6D,CAC7D,YAAa,CACb,cAAe,CACf,kBAAmB,CACnB,6BAAgC,CAChC,0CACE,qDAAwD,CAE5D,YACE,4DAA6D,CAC7D,YAAa,CACb,gBAAiB,CACjB,SAAU,CACV,QAAW,CAEb,4BACE,WAAc,CAEhB,0CACE,qDAAwD,CAE1D,YACE,6DAA8D,CAC9D,YAAa,CACb,qBAAsB,CACtB,WAAc,CAEhB,4BACE,WAAc,CAEhB,0CACE,uDAA0D","file":"patternfly.min.css","sourcesContent":["@charset \"UTF-8\";\n.pf-t-light, .pf-c-accordion, .pf-c-alert, .pf-c-banner.pf-m-info, .pf-c-banner.pf-m-warning, .pf-c-calendar-month, .pf-c-chip, .pf-c-chip-group, .pf-c-context-selector__menu, .pf-c-data-list, .pf-c-form-control, .pf-c-input-group, .pf-c-menu, .pf-c-page__sidebar.pf-m-light, .pf-c-select, .pf-c-table {\n --pf-global--Color--100: var(--pf-global--Color--dark-100);\n --pf-global--Color--200: var(--pf-global--Color--dark-200);\n --pf-global--BorderColor--100: var(--pf-global--BorderColor--dark-100);\n --pf-global--primary-color--100: var(--pf-global--primary-color--dark-100);\n --pf-global--link--Color: var(--pf-global--link--Color--dark);\n --pf-global--link--Color--hover: var(--pf-global--link--Color--dark--hover);\n --pf-global--BackgroundColor--100: var(--pf-global--BackgroundColor--light-100); }\n\n.pf-t-dark, .pf-c-about-modal-box, .pf-c-banner, .pf-c-login__header, .pf-c-login__footer, .pf-c-page__header, .pf-c-page__main-section[class*=\"pf-m-dark-\"], .pf-c-wizard__header {\n --pf-global--Color--100: var(--pf-global--Color--light-100);\n --pf-global--Color--200: var(--pf-global--Color--light-200);\n --pf-global--BorderColor--100: var(--pf-global--BorderColor--light-100);\n --pf-global--primary-color--100: var(--pf-global--primary-color--light-100);\n --pf-global--link--Color: var(--pf-global--link--Color--light);\n --pf-global--link--Color--hover: var(--pf-global--link--Color--light);\n --pf-global--BackgroundColor--100: var(--pf-global--BackgroundColor--dark-100); }\n .pf-t-dark .pf-c-card, .pf-c-about-modal-box .pf-c-card, .pf-c-banner .pf-c-card, .pf-c-login__header .pf-c-card, .pf-c-login__footer .pf-c-card, .pf-c-page__header .pf-c-card, .pf-c-page__main-section[class*=\"pf-m-dark-\"] .pf-c-card, .pf-c-wizard__header .pf-c-card {\n --pf-c-card--BackgroundColor: var(--pf-global--BackgroundColor--dark-transparent-200); }\n .pf-t-dark .pf-c-button, .pf-c-about-modal-box .pf-c-button, .pf-c-banner .pf-c-button, .pf-c-login__header .pf-c-button, .pf-c-login__footer .pf-c-button, .pf-c-page__header .pf-c-button, .pf-c-page__main-section[class*=\"pf-m-dark-\"] .pf-c-button, .pf-c-wizard__header .pf-c-button {\n --pf-c-button--m-primary--Color: var(--pf-global--primary-color--dark-100);\n --pf-c-button--m-primary--hover--Color: var(--pf-global--primary-color--dark-100);\n --pf-c-button--m-primary--focus--Color: var(--pf-global--primary-color--dark-100);\n --pf-c-button--m-primary--active--Color: var(--pf-global--primary-color--dark-100);\n --pf-c-button--m-primary--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-button--m-primary--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-button--m-primary--focus--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-button--m-primary--active--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-button--m-secondary--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--hover--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--focus--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--active--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--BorderColor: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--hover--BorderColor: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--focus--BorderColor: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--active--BorderColor: var(--pf-global--Color--light-100); }\n\n.pf-c-data-list__item-action, .pf-c-page__header-tools-group, .pf-c-page__header-tools-item, .pf-c-table tr > * {\n --pf-hidden-visible--visible--Visibility: visible;\n --pf-hidden-visible--hidden--Display: none;\n --pf-hidden-visible--hidden--Visibility: hidden;\n --pf-hidden-visible--Display: var(--pf-hidden-visible--visible--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--visible--Visibility);\n display: var(--pf-hidden-visible--Display);\n visibility: var(--pf-hidden-visible--Visibility); }\n .pf-m-hidden.pf-c-data-list__item-action, .pf-m-hidden.pf-c-page__header-tools-group, .pf-m-hidden.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-hidden {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--hidden--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--hidden--Visibility); }\n @media screen and (min-width: 576px) {\n .pf-m-hidden-on-sm.pf-c-data-list__item-action, .pf-m-hidden-on-sm.pf-c-page__header-tools-group, .pf-m-hidden-on-sm.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-hidden-on-sm {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--hidden--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--hidden--Visibility); }\n .pf-m-visible-on-sm.pf-c-data-list__item-action, .pf-m-visible-on-sm.pf-c-page__header-tools-group, .pf-m-visible-on-sm.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-visible-on-sm {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--visible--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--visible--Visibility); } }\n @media screen and (min-width: 768px) {\n .pf-m-hidden-on-md.pf-c-data-list__item-action, .pf-m-hidden-on-md.pf-c-page__header-tools-group, .pf-m-hidden-on-md.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-hidden-on-md {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--hidden--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--hidden--Visibility); }\n .pf-m-visible-on-md.pf-c-data-list__item-action, .pf-m-visible-on-md.pf-c-page__header-tools-group, .pf-m-visible-on-md.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-visible-on-md {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--visible--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--visible--Visibility); } }\n @media screen and (min-width: 992px) {\n .pf-m-hidden-on-lg.pf-c-data-list__item-action, .pf-m-hidden-on-lg.pf-c-page__header-tools-group, .pf-m-hidden-on-lg.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-hidden-on-lg {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--hidden--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--hidden--Visibility); }\n .pf-m-visible-on-lg.pf-c-data-list__item-action, .pf-m-visible-on-lg.pf-c-page__header-tools-group, .pf-m-visible-on-lg.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-visible-on-lg {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--visible--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--visible--Visibility); } }\n @media screen and (min-width: 1200px) {\n .pf-m-hidden-on-xl.pf-c-data-list__item-action, .pf-m-hidden-on-xl.pf-c-page__header-tools-group, .pf-m-hidden-on-xl.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-hidden-on-xl {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--hidden--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--hidden--Visibility); }\n .pf-m-visible-on-xl.pf-c-data-list__item-action, .pf-m-visible-on-xl.pf-c-page__header-tools-group, .pf-m-visible-on-xl.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-visible-on-xl {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--visible--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--visible--Visibility); } }\n @media screen and (min-width: 1450px) {\n .pf-m-hidden-on-2xl.pf-c-data-list__item-action, .pf-m-hidden-on-2xl.pf-c-page__header-tools-group, .pf-m-hidden-on-2xl.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-hidden-on-2xl {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--hidden--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--hidden--Visibility); }\n .pf-m-visible-on-2xl.pf-c-data-list__item-action, .pf-m-visible-on-2xl.pf-c-page__header-tools-group, .pf-m-visible-on-2xl.pf-c-page__header-tools-item, .pf-c-table tr > .pf-m-visible-on-2xl {\n --pf-hidden-visible--Display: var(--pf-hidden-visible--visible--Display);\n --pf-hidden-visible--Visibility: var(--pf-hidden-visible--visible--Visibility); } }\n\n:root {\n --pf-global--palette--black-100: #fafafa;\n --pf-global--palette--black-150: #f5f5f5;\n --pf-global--palette--black-200: #f0f0f0;\n --pf-global--palette--black-300: #d2d2d2;\n --pf-global--palette--black-400: #b8bbbe;\n --pf-global--palette--black-500: #8a8d90;\n --pf-global--palette--black-600: #6a6e73;\n --pf-global--palette--black-700: #4f5255;\n --pf-global--palette--black-800: #3c3f42;\n --pf-global--palette--black-850: #212427;\n --pf-global--palette--black-900: #151515;\n --pf-global--palette--black-1000: #030303;\n --pf-global--palette--blue-50: #e7f1fa;\n --pf-global--palette--blue-100: #bee1f4;\n --pf-global--palette--blue-200: #73bcf7;\n --pf-global--palette--blue-300: #2b9af3;\n --pf-global--palette--blue-400: #06c;\n --pf-global--palette--blue-500: #004080;\n --pf-global--palette--blue-600: #002952;\n --pf-global--palette--blue-700: #001223;\n --pf-global--palette--cyan-50: #f2f9f9;\n --pf-global--palette--cyan-100: #a2d9d9;\n --pf-global--palette--cyan-200: #73c5c5;\n --pf-global--palette--cyan-300: #009596;\n --pf-global--palette--cyan-400: #005f60;\n --pf-global--palette--cyan-500: #003737;\n --pf-global--palette--cyan-600: #002323;\n --pf-global--palette--cyan-700: #000f0f;\n --pf-global--palette--gold-50: #fdf7e7;\n --pf-global--palette--gold-100: #f9e0a2;\n --pf-global--palette--gold-200: #f6d173;\n --pf-global--palette--gold-300: #f4c145;\n --pf-global--palette--gold-400: #f0ab00;\n --pf-global--palette--gold-500: #c58c00;\n --pf-global--palette--gold-600: #795600;\n --pf-global--palette--gold-700: #3d2c00;\n --pf-global--palette--green-50: #f3faf2;\n --pf-global--palette--green-100: #bde5b8;\n --pf-global--palette--green-200: #95d58e;\n --pf-global--palette--green-300: #6ec664;\n --pf-global--palette--green-400: #5ba352;\n --pf-global--palette--green-500: #3e8635;\n --pf-global--palette--green-600: #1e4f18;\n --pf-global--palette--green-700: #0f280d;\n --pf-global--palette--light-blue-100: #beedf9;\n --pf-global--palette--light-blue-200: #7cdbf3;\n --pf-global--palette--light-blue-300: #35caed;\n --pf-global--palette--light-blue-400: #00b9e4;\n --pf-global--palette--light-blue-500: #008bad;\n --pf-global--palette--light-blue-600: #005c73;\n --pf-global--palette--light-blue-700: #002d39;\n --pf-global--palette--light-green-100: #e4f5bc;\n --pf-global--palette--light-green-200: #c8eb79;\n --pf-global--palette--light-green-300: #ace12e;\n --pf-global--palette--light-green-400: #92d400;\n --pf-global--palette--light-green-500: #6ca100;\n --pf-global--palette--light-green-600: #486b00;\n --pf-global--palette--light-green-700: #253600;\n --pf-global--palette--orange-100: #f4b678;\n --pf-global--palette--orange-200: #ef9234;\n --pf-global--palette--orange-300: #ec7a08;\n --pf-global--palette--orange-400: #c46100;\n --pf-global--palette--orange-500: #8f4700;\n --pf-global--palette--orange-600: #773d00;\n --pf-global--palette--orange-700: #3b1f00;\n --pf-global--palette--purple-50: #f2f0fc;\n --pf-global--palette--purple-100: #cbc1ff;\n --pf-global--palette--purple-200: #b2a3ff;\n --pf-global--palette--purple-300: #a18fff;\n --pf-global--palette--purple-400: #8476d1;\n --pf-global--palette--purple-500: #6753ac;\n --pf-global--palette--purple-600: #40199a;\n --pf-global--palette--purple-700: #1f0066;\n --pf-global--palette--red-50: #faeae8;\n --pf-global--palette--red-100: #c9190b;\n --pf-global--palette--red-200: #a30000;\n --pf-global--palette--red-300: #7d1007;\n --pf-global--palette--red-400: #470000;\n --pf-global--palette--red-500: #2c0000;\n --pf-global--palette--white: #fff;\n --pf-global--BackgroundColor--100: #fff;\n --pf-global--BackgroundColor--200: #f0f0f0;\n --pf-global--BackgroundColor--light-100: #fff;\n --pf-global--BackgroundColor--light-200: #fafafa;\n --pf-global--BackgroundColor--light-300: #f0f0f0;\n --pf-global--BackgroundColor--dark-100: #151515;\n --pf-global--BackgroundColor--dark-200: #3c3f42;\n --pf-global--BackgroundColor--dark-300: #212427;\n --pf-global--BackgroundColor--dark-400: #4f5255;\n --pf-global--BackgroundColor--dark-transparent-100: rgba(3, 3, 3, 0.62);\n --pf-global--BackgroundColor--dark-transparent-200: rgba(3, 3, 3, 0.32);\n --pf-global--Color--100: #151515;\n --pf-global--Color--200: #6a6e73;\n --pf-global--Color--300: #3c3f42;\n --pf-global--Color--400: #8a8d90;\n --pf-global--Color--light-100: #fff;\n --pf-global--Color--light-200: #f0f0f0;\n --pf-global--Color--light-300: #d2d2d2;\n --pf-global--Color--dark-100: #151515;\n --pf-global--Color--dark-200: #6a6e73;\n --pf-global--active-color--100: #06c;\n --pf-global--active-color--200: #bee1f4;\n --pf-global--active-color--300: #2b9af3;\n --pf-global--active-color--400: #73bcf7;\n --pf-global--disabled-color--100: #6a6e73;\n --pf-global--disabled-color--200: #d2d2d2;\n --pf-global--disabled-color--300: #f0f0f0;\n --pf-global--primary-color--100: #06c;\n --pf-global--primary-color--200: #004080;\n --pf-global--primary-color--light-100: #73bcf7;\n --pf-global--primary-color--dark-100: #06c;\n --pf-global--secondary-color--100: #6a6e73;\n --pf-global--default-color--100: #73c5c5;\n --pf-global--default-color--200: #009596;\n --pf-global--default-color--300: #003737;\n --pf-global--success-color--100: #3e8635;\n --pf-global--success-color--200: #1e4f18;\n --pf-global--info-color--100: #2b9af3;\n --pf-global--info-color--200: #002952;\n --pf-global--warning-color--100: #f0ab00;\n --pf-global--warning-color--200: #795600;\n --pf-global--danger-color--100: #c9190b;\n --pf-global--danger-color--200: #a30000;\n --pf-global--danger-color--300: #470000;\n --pf-global--BoxShadow--sm: 0 0.0625rem 0.125rem 0 rgba(3, 3, 3, 0.12), 0 0 0.125rem 0 rgba(3, 3, 3, 0.06);\n --pf-global--BoxShadow--sm-top: 0 -0.125rem 0.25rem -0.0625rem rgba(3, 3, 3, 0.16);\n --pf-global--BoxShadow--sm-right: 0.125rem 0 0.25rem -0.0625rem rgba(3, 3, 3, 0.16);\n --pf-global--BoxShadow--sm-bottom: 0 0.125rem 0.25rem -0.0625rem rgba(3, 3, 3, 0.16);\n --pf-global--BoxShadow--sm-left: -0.125rem 0 0.25rem -0.0625rem rgba(3, 3, 3, 0.16);\n --pf-global--BoxShadow--md: 0 0.25rem 0.5rem 0rem rgba(3, 3, 3, 0.12), 0 0 0.25rem 0 rgba(3, 3, 3, 0.06);\n --pf-global--BoxShadow--md-top: 0 -0.5rem 0.5rem -0.375rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--md-right: 0.5rem 0 0.5rem -0.375rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--md-bottom: 0 0.5rem 0.5rem -0.375rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--md-left: -0.5rem 0 0.5rem -0.375rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--lg: 0 0.5rem 1rem 0 rgba(3, 3, 3, 0.16), 0 0 0.375rem 0 rgba(3, 3, 3, 0.08);\n --pf-global--BoxShadow--lg-top: 0 -0.75rem 0.75rem -0.5rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--lg-right: 0.75rem 0 0.75rem -0.5rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--lg-bottom: 0 0.75rem 0.75rem -0.5rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--lg-left: -0.75rem 0 0.75rem -0.5rem rgba(3, 3, 3, 0.18);\n --pf-global--BoxShadow--xl: 0 1rem 2rem 0 rgba(3, 3, 3, 0.16), 0 0 0.5rem 0 rgba(3, 3, 3, 0.1);\n --pf-global--BoxShadow--xl-top: 0 -1rem 1rem -0.5rem rgba(3, 3, 3, 0.2);\n --pf-global--BoxShadow--xl-right: 1rem 0 1rem -0.5rem rgba(3, 3, 3, 0.2);\n --pf-global--BoxShadow--xl-bottom: 0 1rem 1rem -0.5rem rgba(3, 3, 3, 0.2);\n --pf-global--BoxShadow--xl-left: -1rem 0 1rem -0.5rem rgba(3, 3, 3, 0.2);\n --pf-global--BoxShadow--inset: inset 0 0 0.625rem 0 rgba(3, 3, 3, 0.25);\n --pf-global--font-path: \"./assets/fonts\";\n --pf-global--fonticon-path: \"./assets/pficon\";\n --pf-global--spacer--xs: 0.25rem;\n --pf-global--spacer--sm: 0.5rem;\n --pf-global--spacer--md: 1rem;\n --pf-global--spacer--lg: 1.5rem;\n --pf-global--spacer--xl: 2rem;\n --pf-global--spacer--2xl: 3rem;\n --pf-global--spacer--3xl: 4rem;\n --pf-global--spacer--4xl: 5rem;\n --pf-global--spacer--form-element: 0.375rem;\n --pf-global--gutter: 1rem;\n --pf-global--gutter--md: 1.5rem;\n --pf-global--ZIndex--xs: 100;\n --pf-global--ZIndex--sm: 200;\n --pf-global--ZIndex--md: 300;\n --pf-global--ZIndex--lg: 400;\n --pf-global--ZIndex--xl: 500;\n --pf-global--ZIndex--2xl: 600;\n --pf-global--breakpoint--xs: 0;\n --pf-global--breakpoint--sm: 576px;\n --pf-global--breakpoint--md: 768px;\n --pf-global--breakpoint--lg: 992px;\n --pf-global--breakpoint--xl: 1200px;\n --pf-global--breakpoint--2xl: 1450px;\n --pf-global--link--Color: #06c;\n --pf-global--link--Color--hover: #004080;\n --pf-global--link--Color--light: #2b9af3;\n --pf-global--link--Color--light--hover: #73bcf7;\n --pf-global--link--Color--dark: #06c;\n --pf-global--link--Color--dark--hover: #004080;\n --pf-global--link--TextDecoration: none;\n --pf-global--link--TextDecoration--hover: underline;\n --pf-global--BorderWidth--sm: 1px;\n --pf-global--BorderWidth--md: 2px;\n --pf-global--BorderWidth--lg: 3px;\n --pf-global--BorderWidth--xl: 4px;\n --pf-global--BorderColor--100: #d2d2d2;\n --pf-global--BorderColor--200: #8a8d90;\n --pf-global--BorderColor--300: #f0f0f0;\n --pf-global--BorderColor--dark-100: #d2d2d2;\n --pf-global--BorderColor--light-100: #b8bbbe;\n --pf-global--BorderRadius--sm: 3px;\n --pf-global--BorderRadius--lg: 30em;\n --pf-global--icon--Color--light: #6a6e73;\n --pf-global--icon--Color--dark: #151515;\n --pf-global--icon--FontSize--sm: 0.625rem;\n --pf-global--icon--FontSize--md: 1.125rem;\n --pf-global--icon--FontSize--lg: 1.5rem;\n --pf-global--icon--FontSize--xl: 3.375rem;\n --pf-global--FontFamily--sans-serif: \"RedHatText\", \"Overpass\", overpass, helvetica, arial, sans-serif;\n --pf-global--FontFamily--heading--sans-serif: \"RedHatDisplay\", \"Overpass\", overpass, helvetica, arial, sans-serif;\n --pf-global--FontFamily--monospace: \"Liberation Mono\", consolas, \"SFMono-Regular\", menlo, monaco, \"Courier New\", monospace;\n --pf-global--FontFamily--overpass--sans-serif: \"overpass\", overpass, \"open sans\", -apple-system, blinkmacsystemfont, \"Segoe UI\", roboto, \"Helvetica Neue\", arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n --pf-global--FontFamily--overpass--monospace: \"overpass-mono\", overpass-mono, \"SFMono-Regular\", menlo, monaco, consolas, \"Liberation Mono\", \"Courier New\", monospace;\n --pf-global--FontSize--4xl: 2.25rem;\n --pf-global--FontSize--3xl: 1.75rem;\n --pf-global--FontSize--2xl: 1.5rem;\n --pf-global--FontSize--xl: 1.25rem;\n --pf-global--FontSize--lg: 1.125rem;\n --pf-global--FontSize--md: 1rem;\n --pf-global--FontSize--sm: 0.875rem;\n --pf-global--FontSize--xs: 0.75rem;\n --pf-global--FontWeight--light: 300;\n --pf-global--FontWeight--normal: 400;\n --pf-global--FontWeight--semi-bold: 700;\n --pf-global--FontWeight--overpass--semi-bold: 500;\n --pf-global--FontWeight--bold: 700;\n --pf-global--FontWeight--overpass--bold: 600;\n --pf-global--LineHeight--sm: 1.3;\n --pf-global--LineHeight--md: 1.5;\n --pf-global--ListStyle: disc outside;\n --pf-global--Transition: all 250ms cubic-bezier(0.42, 0, 0.58, 1);\n --pf-global--TimingFunction: cubic-bezier(0.645, 0.045, 0.355, 1);\n --pf-global--TransitionDuration: 250ms;\n --pf-global--arrow--width: 0.9375rem;\n --pf-global--arrow--width-lg: 1.5625rem;\n --pf-global--target-size--MinWidth: 44px;\n --pf-global--target-size--MinHeight: 44px; }\n\n.pf-m-overpass-font {\n --pf-global--FontFamily--sans-serif: var(--pf-global--FontFamily--overpass--sans-serif);\n --pf-global--FontFamily--heading--sans-serif: var(--pf-global--FontFamily--sans-serif);\n --pf-global--FontFamily--monospace: var(--pf-global--FontFamily--overpass--monospace);\n --pf-global--FontWeight--semi-bold: var(--pf-global--FontWeight--overpass--semi-bold);\n --pf-global--FontWeight--bold: var(--pf-global--FontWeight--overpass--bold); }\n\n@font-face {\n font-family: \"RedHatDisplay\";\n src: url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Regular.eot\");\n src: url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Regular.woff\") format(\"woff\");\n font-style: normal;\n font-weight: 300;\n text-rendering: optimizeLegibility; }\n\n@font-face {\n font-family: \"RedHatDisplay\";\n src: url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Medium.eot\");\n src: url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Medium.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Medium.woff\") format(\"woff\");\n font-style: normal;\n font-weight: 400;\n text-rendering: optimizeLegibility; }\n\n@font-face {\n font-family: \"RedHatDisplay\";\n src: url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Bold.eot\");\n src: url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Bold.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/RedHatDisplay/RedHatDisplay-Bold.woff\") format(\"woff\");\n font-style: normal;\n font-weight: 700;\n text-rendering: optimizeLegibility; }\n\n@font-face {\n font-family: \"RedHatText\";\n src: url(\"./assets/fonts/RedHatText/RedHatText-Regular.eot\");\n src: url(\"./assets/fonts/RedHatText/RedHatText-Regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/RedHatText/RedHatText-Regular.woff\") format(\"woff\");\n font-style: normal;\n font-weight: 400;\n text-rendering: optimizeLegibility; }\n\n@font-face {\n font-family: \"RedHatText\";\n src: url(\"./assets/fonts/RedHatText/RedHatText-Medium.eot\");\n src: url(\"./assets/fonts/RedHatText/RedHatText-Medium.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/RedHatText/RedHatText-Medium.woff\") format(\"woff\");\n font-style: normal;\n font-weight: 700;\n text-rendering: optimizeLegibility; }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 200;\n src: url(\"./assets/fonts/overpass-webfont/overpass-thin.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-thin.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-thin.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-thin.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-thin.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 200;\n src: url(\"./assets/fonts/overpass-webfont/overpass-thin-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-thin-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-thin-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-thin-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-thin-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 300;\n src: url(\"./assets/fonts/overpass-webfont/overpass-extralight.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-extralight.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-extralight.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-extralight.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-extralight.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 300;\n src: url(\"./assets/fonts/overpass-webfont/overpass-extralight-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-extralight-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-extralight-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-extralight-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-extralight-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 400;\n src: url(\"./assets/fonts/overpass-webfont/overpass-light.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-light.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-light.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-light.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-light.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 400;\n src: url(\"./assets/fonts/overpass-webfont/overpass-light-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-light-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-light-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-light-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-light-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 500;\n src: url(\"./assets/fonts/overpass-webfont/overpass-regular.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-regular.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-regular.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-regular.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 500;\n src: url(\"./assets/fonts/overpass-webfont/overpass-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 600;\n src: url(\"./assets/fonts/overpass-webfont/overpass-semibold.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-semibold.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-semibold.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-semibold.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-semibold.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 600;\n src: url(\"./assets/fonts/overpass-webfont/overpass-semibold-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-semibold-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-semibold-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-semibold-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-semibold-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 700;\n src: url(\"./assets/fonts/overpass-webfont/overpass-bold.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-bold.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-bold.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-bold.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-bold.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 700;\n src: url(\"./assets/fonts/overpass-webfont/overpass-bold-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-bold-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-bold-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-bold-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-bold-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 800;\n src: url(\"./assets/fonts/overpass-webfont/overpass-extrabold.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-extrabold.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-extrabold.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-extrabold.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-extrabold.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 800;\n src: url(\"./assets/fonts/overpass-webfont/overpass-extrabold-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-extrabold-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-extrabold-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-extrabold-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-extrabold-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: normal;\n font-weight: 900;\n src: url(\"./assets/fonts/overpass-webfont/overpass-heavy.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-heavy.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-heavy.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-heavy.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-heavy.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass\";\n font-style: italic;\n font-weight: 900;\n src: url(\"./assets/fonts/overpass-webfont/overpass-heavy-italic.eot\");\n src: url(\"./assets/fonts/overpass-webfont/overpass-heavy-italic.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-webfont/overpass-heavy-italic.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-webfont/overpass-heavy-italic.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-webfont/overpass-heavy-italic.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass-mono\";\n font-style: normal;\n font-weight: 300;\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-light.eot\");\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-light.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-light.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-light.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-light.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass-mono\";\n font-style: normal;\n font-weight: 400;\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-regular.eot\");\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-regular.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-regular.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-regular.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-regular.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass-mono\";\n font-style: normal;\n font-weight: 500;\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-semibold.eot\");\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-semibold.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-semibold.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-semibold.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-semibold.ttf\") format(\"truetype\"); }\n\n@font-face {\n font-family: \"overpass-mono\";\n font-style: normal;\n font-weight: 600;\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-bold.eot\");\n src: url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-bold.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-bold.woff2\") format(\"woff2\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-bold.woff\") format(\"woff\"), url(\"./assets/fonts/overpass-mono-webfont/overpass-mono-bold.ttf\") format(\"truetype\"); }\n\n[class*=\"pf-c-\"], [class*=\"pf-c-\"]::before, [class*=\"pf-c-\"]::after {\n padding: 0;\n margin: 0;\n background-color: transparent; }\n\nhtml {\n font-size: unset !important; }\n\n.pf-screen-reader {\n position: fixed;\n top: 0;\n left: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0; }\n\nhtml,\nbody,\np,\nol,\nul,\nli,\ndl,\ndt,\ndd,\nblockquote,\nfigure,\nfieldset,\nlegend,\ntextarea,\npre,\niframe,\nhr,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n padding: 0;\n margin: 0; }\n\nhtml,\nbody {\n height: 100%; }\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: 100%;\n font-weight: var(--pf-global--FontWeight--normal); }\n\nul {\n list-style: none; }\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: 100%;\n line-height: var(--pf-global--LineHeight--md);\n color: var(--pf-global--Color--100); }\n\nimg,\nembed,\niframe,\nobject,\naudio,\nvideo {\n max-width: 100%;\n height: auto; }\n\niframe {\n border: 0; }\n\ntable {\n border-spacing: 0;\n border-collapse: collapse; }\n\ntd,\nth {\n padding: 0;\n text-align: left; }\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; }\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15; }\n\nbody {\n font-family: var(--pf-global--FontFamily--sans-serif);\n font-size: var(--pf-global--FontSize--md);\n font-weight: var(--pf-global--FontWeight--normal);\n line-height: var(--pf-global--LineHeight--md);\n text-align: left;\n background-color: var(--pf-global--BackgroundColor--100); }\n\na {\n font-weight: var(--pf-global--link--FontWeight);\n color: var(--pf-global--link--Color);\n text-decoration: var(--pf-global--link--TextDecoration); }\n a:hover {\n --pf-global--link--Color: var(--pf-global--link--Color--hover);\n --pf-global--link--TextDecoration: var(--pf-global--link--TextDecoration--hover); }\n\nbutton,\na {\n cursor: pointer; }\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none; }\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText; }\n\n.pf-m-overpass-font a {\n font-weight: var(--pf-global--FontWeight--semi-bold); }\n\n.pf-t-dark.pf-m-transparent {\n background-color: transparent; }\n\n.pf-t-dark.pf-m-transparent-100 {\n background-color: rgba(3, 3, 3, 0.42); }\n\n.pf-t-dark.pf-m-transparent-200 {\n background-color: rgba(3, 3, 3, 0.6); }\n\n.pf-t-dark.pf-m-opaque-100 {\n background-color: #3c3f42; }\n\n.pf-t-dark.pf-m-opaque-200 {\n background-color: #151515; }\n\n.pf-t-light.pf-m-transparent {\n background-color: transparent; }\n\n.pf-t-light.pf-m-opaque-100 {\n background-color: #fff; }\n\n.pf-t-light.pf-m-opaque-200 {\n background-color: #fafafa; }\n\n.pf-t-light.pf-m-opaque-300 {\n background-color: #f0f0f0; }\n\n* .fa,\n* .fas,\n* .far,\n* .fal,\n* .fab {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n display: inline-block;\n font-style: normal;\n font-variant: normal;\n text-rendering: auto;\n line-height: 1; }\n\n* .fa-lg {\n font-size: 1.33333em;\n line-height: 0.75em;\n vertical-align: -.0667em; }\n\n* .fa-xs {\n font-size: .75em; }\n\n* .fa-sm {\n font-size: .875em; }\n\n* .fa-1x {\n font-size: 1em; }\n\n* .fa-2x {\n font-size: 2em; }\n\n* .fa-3x {\n font-size: 3em; }\n\n* .fa-4x {\n font-size: 4em; }\n\n* .fa-5x {\n font-size: 5em; }\n\n* .fa-6x {\n font-size: 6em; }\n\n* .fa-7x {\n font-size: 7em; }\n\n* .fa-8x {\n font-size: 8em; }\n\n* .fa-9x {\n font-size: 9em; }\n\n* .fa-10x {\n font-size: 10em; }\n\n* .fa-fw {\n text-align: center;\n width: 1.25em; }\n\n* .fa-ul {\n list-style-type: none;\n margin-left: 2.5em;\n padding-left: 0; }\n * .fa-ul > li {\n position: relative; }\n\n* .fa-li {\n left: -2em;\n position: absolute;\n text-align: center;\n width: 2em;\n line-height: inherit; }\n\n* .fa-border {\n border: solid 0.08em #eee;\n border-radius: .1em;\n padding: .2em .25em .15em; }\n\n* .fa-pull-left {\n float: left; }\n\n* .fa-pull-right {\n float: right; }\n\n* .fa.fa-pull-left,\n* .fas.fa-pull-left,\n* .far.fa-pull-left,\n* .fal.fa-pull-left,\n* .fab.fa-pull-left {\n margin-right: .3em; }\n\n* .fa.fa-pull-right,\n* .fas.fa-pull-right,\n* .far.fa-pull-right,\n* .fal.fa-pull-right,\n* .fab.fa-pull-right {\n margin-left: .3em; }\n\n* .fa-spin {\n animation: fa-spin 2s infinite linear; }\n\n* .fa-pulse {\n animation: fa-spin 1s infinite steps(8); }\n\n@keyframes fa-spin {\n 0% {\n transform: rotate(0deg); }\n 100% {\n transform: rotate(360deg); } }\n\n* .fa-rotate-90 {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)\";\n transform: rotate(90deg); }\n\n* .fa-rotate-180 {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)\";\n transform: rotate(180deg); }\n\n* .fa-rotate-270 {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)\";\n transform: rotate(270deg); }\n\n* .fa-flip-horizontal {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)\";\n transform: scale(-1, 1); }\n\n* .fa-flip-vertical {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n transform: scale(1, -1); }\n\n* .fa-flip-horizontal.fa-flip-vertical {\n -ms-filter: \"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)\";\n transform: scale(-1, -1); }\n\n* :root .fa-rotate-90,\n* :root .fa-rotate-180,\n* :root .fa-rotate-270,\n* :root .fa-flip-horizontal,\n* :root .fa-flip-vertical {\n filter: none; }\n\n* .fa-stack {\n display: inline-block;\n height: 2em;\n line-height: 2em;\n position: relative;\n vertical-align: middle;\n width: 2.5em; }\n\n* .fa-stack-1x,\n* .fa-stack-2x {\n left: 0;\n position: absolute;\n text-align: center;\n width: 100%; }\n\n* .fa-stack-1x {\n line-height: inherit; }\n\n\n* .fa-stack-2x {\n font-size: 2em; }\n\n* .fa-inverse {\n color: #fff; }\n\n* .fa-500px:before {\n content: \"\\f26e\"; }\n\n* .fa-accessible-icon:before {\n content: \"\\f368\"; }\n\n* .fa-accusoft:before {\n content: \"\\f369\"; }\n\n* .fa-acquisitions-incorporated:before {\n content: \"\\f6af\"; }\n\n* .fa-ad:before {\n content: \"\\f641\"; }\n\n* .fa-address-book:before {\n content: \"\\f2b9\"; }\n\n* .fa-address-card:before {\n content: \"\\f2bb\"; }\n\n* .fa-adjust:before {\n content: \"\\f042\"; }\n\n* .fa-adn:before {\n content: \"\\f170\"; }\n\n* .fa-adobe:before {\n content: \"\\f778\"; }\n\n* .fa-adversal:before {\n content: \"\\f36a\"; }\n\n* .fa-affiliatetheme:before {\n content: \"\\f36b\"; }\n\n* .fa-air-freshener:before {\n content: \"\\f5d0\"; }\n\n* .fa-algolia:before {\n content: \"\\f36c\"; }\n\n* .fa-align-center:before {\n content: \"\\f037\"; }\n\n* .fa-align-justify:before {\n content: \"\\f039\"; }\n\n* .fa-align-left:before {\n content: \"\\f036\"; }\n\n* .fa-align-right:before {\n content: \"\\f038\"; }\n\n* .fa-alipay:before {\n content: \"\\f642\"; }\n\n* .fa-allergies:before {\n content: \"\\f461\"; }\n\n* .fa-amazon:before {\n content: \"\\f270\"; }\n\n* .fa-amazon-pay:before {\n content: \"\\f42c\"; }\n\n* .fa-ambulance:before {\n content: \"\\f0f9\"; }\n\n* .fa-american-sign-language-interpreting:before {\n content: \"\\f2a3\"; }\n\n* .fa-amilia:before {\n content: \"\\f36d\"; }\n\n* .fa-anchor:before {\n content: \"\\f13d\"; }\n\n* .fa-android:before {\n content: \"\\f17b\"; }\n\n* .fa-angellist:before {\n content: \"\\f209\"; }\n\n* .fa-angle-double-down:before {\n content: \"\\f103\"; }\n\n* .fa-angle-double-left:before {\n content: \"\\f100\"; }\n\n* .fa-angle-double-right:before {\n content: \"\\f101\"; }\n\n* .fa-angle-double-up:before {\n content: \"\\f102\"; }\n\n* .fa-angle-down:before {\n content: \"\\f107\"; }\n\n* .fa-angle-left:before {\n content: \"\\f104\"; }\n\n* .fa-angle-right:before {\n content: \"\\f105\"; }\n\n* .fa-angle-up:before {\n content: \"\\f106\"; }\n\n* .fa-angry:before {\n content: \"\\f556\"; }\n\n* .fa-angrycreative:before {\n content: \"\\f36e\"; }\n\n* .fa-angular:before {\n content: \"\\f420\"; }\n\n* .fa-ankh:before {\n content: \"\\f644\"; }\n\n* .fa-app-store:before {\n content: \"\\f36f\"; }\n\n* .fa-app-store-ios:before {\n content: \"\\f370\"; }\n\n* .fa-apper:before {\n content: \"\\f371\"; }\n\n* .fa-apple:before {\n content: \"\\f179\"; }\n\n* .fa-apple-alt:before {\n content: \"\\f5d1\"; }\n\n* .fa-apple-pay:before {\n content: \"\\f415\"; }\n\n* .fa-archive:before {\n content: \"\\f187\"; }\n\n* .fa-archway:before {\n content: \"\\f557\"; }\n\n* .fa-arrow-alt-circle-down:before {\n content: \"\\f358\"; }\n\n* .fa-arrow-alt-circle-left:before {\n content: \"\\f359\"; }\n\n* .fa-arrow-alt-circle-right:before {\n content: \"\\f35a\"; }\n\n* .fa-arrow-alt-circle-up:before {\n content: \"\\f35b\"; }\n\n* .fa-arrow-circle-down:before {\n content: \"\\f0ab\"; }\n\n* .fa-arrow-circle-left:before {\n content: \"\\f0a8\"; }\n\n* .fa-arrow-circle-right:before {\n content: \"\\f0a9\"; }\n\n* .fa-arrow-circle-up:before {\n content: \"\\f0aa\"; }\n\n* .fa-arrow-down:before {\n content: \"\\f063\"; }\n\n* .fa-arrow-left:before {\n content: \"\\f060\"; }\n\n* .fa-arrow-right:before {\n content: \"\\f061\"; }\n\n* .fa-arrow-up:before {\n content: \"\\f062\"; }\n\n* .fa-arrows-alt:before {\n content: \"\\f0b2\"; }\n\n* .fa-arrows-alt-h:before {\n content: \"\\f337\"; }\n\n* .fa-arrows-alt-v:before {\n content: \"\\f338\"; }\n\n* .fa-artstation:before {\n content: \"\\f77a\"; }\n\n* .fa-assistive-listening-systems:before {\n content: \"\\f2a2\"; }\n\n* .fa-asterisk:before {\n content: \"\\f069\"; }\n\n* .fa-asymmetrik:before {\n content: \"\\f372\"; }\n\n* .fa-at:before {\n content: \"\\f1fa\"; }\n\n* .fa-atlas:before {\n content: \"\\f558\"; }\n\n* .fa-atlassian:before {\n content: \"\\f77b\"; }\n\n* .fa-atom:before {\n content: \"\\f5d2\"; }\n\n* .fa-audible:before {\n content: \"\\f373\"; }\n\n* .fa-audio-description:before {\n content: \"\\f29e\"; }\n\n* .fa-autoprefixer:before {\n content: \"\\f41c\"; }\n\n* .fa-avianex:before {\n content: \"\\f374\"; }\n\n* .fa-aviato:before {\n content: \"\\f421\"; }\n\n* .fa-award:before {\n content: \"\\f559\"; }\n\n* .fa-aws:before {\n content: \"\\f375\"; }\n\n* .fa-baby:before {\n content: \"\\f77c\"; }\n\n* .fa-baby-carriage:before {\n content: \"\\f77d\"; }\n\n* .fa-backspace:before {\n content: \"\\f55a\"; }\n\n* .fa-backward:before {\n content: \"\\f04a\"; }\n\n* .fa-balance-scale:before {\n content: \"\\f24e\"; }\n\n* .fa-ban:before {\n content: \"\\f05e\"; }\n\n* .fa-band-aid:before {\n content: \"\\f462\"; }\n\n* .fa-bandcamp:before {\n content: \"\\f2d5\"; }\n\n* .fa-barcode:before {\n content: \"\\f02a\"; }\n\n* .fa-bars:before {\n content: \"\\f0c9\"; }\n\n* .fa-baseball-ball:before {\n content: \"\\f433\"; }\n\n* .fa-basketball-ball:before {\n content: \"\\f434\"; }\n\n* .fa-bath:before {\n content: \"\\f2cd\"; }\n\n* .fa-battery-empty:before {\n content: \"\\f244\"; }\n\n* .fa-battery-full:before {\n content: \"\\f240\"; }\n\n* .fa-battery-half:before {\n content: \"\\f242\"; }\n\n* .fa-battery-quarter:before {\n content: \"\\f243\"; }\n\n* .fa-battery-three-quarters:before {\n content: \"\\f241\"; }\n\n* .fa-bed:before {\n content: \"\\f236\"; }\n\n* .fa-beer:before {\n content: \"\\f0fc\"; }\n\n* .fa-behance:before {\n content: \"\\f1b4\"; }\n\n* .fa-behance-square:before {\n content: \"\\f1b5\"; }\n\n* .fa-bell:before {\n content: \"\\f0f3\"; }\n\n* .fa-bell-slash:before {\n content: \"\\f1f6\"; }\n\n* .fa-bezier-curve:before {\n content: \"\\f55b\"; }\n\n* .fa-bible:before {\n content: \"\\f647\"; }\n\n* .fa-bicycle:before {\n content: \"\\f206\"; }\n\n* .fa-bimobject:before {\n content: \"\\f378\"; }\n\n* .fa-binoculars:before {\n content: \"\\f1e5\"; }\n\n* .fa-biohazard:before {\n content: \"\\f780\"; }\n\n* .fa-birthday-cake:before {\n content: \"\\f1fd\"; }\n\n* .fa-bitbucket:before {\n content: \"\\f171\"; }\n\n* .fa-bitcoin:before {\n content: \"\\f379\"; }\n\n* .fa-bity:before {\n content: \"\\f37a\"; }\n\n* .fa-black-tie:before {\n content: \"\\f27e\"; }\n\n* .fa-blackberry:before {\n content: \"\\f37b\"; }\n\n* .fa-blender:before {\n content: \"\\f517\"; }\n\n* .fa-blender-phone:before {\n content: \"\\f6b6\"; }\n\n* .fa-blind:before {\n content: \"\\f29d\"; }\n\n* .fa-blog:before {\n content: \"\\f781\"; }\n\n* .fa-blogger:before {\n content: \"\\f37c\"; }\n\n* .fa-blogger-b:before {\n content: \"\\f37d\"; }\n\n* .fa-bluetooth:before {\n content: \"\\f293\"; }\n\n* .fa-bluetooth-b:before {\n content: \"\\f294\"; }\n\n* .fa-bold:before {\n content: \"\\f032\"; }\n\n* .fa-bolt:before {\n content: \"\\f0e7\"; }\n\n* .fa-bomb:before {\n content: \"\\f1e2\"; }\n\n* .fa-bone:before {\n content: \"\\f5d7\"; }\n\n* .fa-bong:before {\n content: \"\\f55c\"; }\n\n* .fa-book:before {\n content: \"\\f02d\"; }\n\n* .fa-book-dead:before {\n content: \"\\f6b7\"; }\n\n* .fa-book-open:before {\n content: \"\\f518\"; }\n\n* .fa-book-reader:before {\n content: \"\\f5da\"; }\n\n* .fa-bookmark:before {\n content: \"\\f02e\"; }\n\n* .fa-bowling-ball:before {\n content: \"\\f436\"; }\n\n* .fa-box:before {\n content: \"\\f466\"; }\n\n* .fa-box-open:before {\n content: \"\\f49e\"; }\n\n* .fa-boxes:before {\n content: \"\\f468\"; }\n\n* .fa-braille:before {\n content: \"\\f2a1\"; }\n\n* .fa-brain:before {\n content: \"\\f5dc\"; }\n\n* .fa-briefcase:before {\n content: \"\\f0b1\"; }\n\n* .fa-briefcase-medical:before {\n content: \"\\f469\"; }\n\n* .fa-broadcast-tower:before {\n content: \"\\f519\"; }\n\n* .fa-broom:before {\n content: \"\\f51a\"; }\n\n* .fa-brush:before {\n content: \"\\f55d\"; }\n\n* .fa-btc:before {\n content: \"\\f15a\"; }\n\n* .fa-bug:before {\n content: \"\\f188\"; }\n\n* .fa-building:before {\n content: \"\\f1ad\"; }\n\n* .fa-bullhorn:before {\n content: \"\\f0a1\"; }\n\n* .fa-bullseye:before {\n content: \"\\f140\"; }\n\n* .fa-burn:before {\n content: \"\\f46a\"; }\n\n* .fa-buromobelexperte:before {\n content: \"\\f37f\"; }\n\n* .fa-bus:before {\n content: \"\\f207\"; }\n\n* .fa-bus-alt:before {\n content: \"\\f55e\"; }\n\n* .fa-business-time:before {\n content: \"\\f64a\"; }\n\n* .fa-buysellads:before {\n content: \"\\f20d\"; }\n\n* .fa-calculator:before {\n content: \"\\f1ec\"; }\n\n* .fa-calendar:before {\n content: \"\\f133\"; }\n\n* .fa-calendar-alt:before {\n content: \"\\f073\"; }\n\n* .fa-calendar-check:before {\n content: \"\\f274\"; }\n\n* .fa-calendar-day:before {\n content: \"\\f783\"; }\n\n* .fa-calendar-minus:before {\n content: \"\\f272\"; }\n\n* .fa-calendar-plus:before {\n content: \"\\f271\"; }\n\n* .fa-calendar-times:before {\n content: \"\\f273\"; }\n\n* .fa-calendar-week:before {\n content: \"\\f784\"; }\n\n* .fa-camera:before {\n content: \"\\f030\"; }\n\n* .fa-camera-retro:before {\n content: \"\\f083\"; }\n\n* .fa-campground:before {\n content: \"\\f6bb\"; }\n\n* .fa-canadian-maple-leaf:before {\n content: \"\\f785\"; }\n\n* .fa-candy-cane:before {\n content: \"\\f786\"; }\n\n* .fa-cannabis:before {\n content: \"\\f55f\"; }\n\n* .fa-capsules:before {\n content: \"\\f46b\"; }\n\n* .fa-car:before {\n content: \"\\f1b9\"; }\n\n* .fa-car-alt:before {\n content: \"\\f5de\"; }\n\n* .fa-car-battery:before {\n content: \"\\f5df\"; }\n\n* .fa-car-crash:before {\n content: \"\\f5e1\"; }\n\n* .fa-car-side:before {\n content: \"\\f5e4\"; }\n\n* .fa-caret-down:before {\n content: \"\\f0d7\"; }\n\n* .fa-caret-left:before {\n content: \"\\f0d9\"; }\n\n* .fa-caret-right:before {\n content: \"\\f0da\"; }\n\n* .fa-caret-square-down:before {\n content: \"\\f150\"; }\n\n* .fa-caret-square-left:before {\n content: \"\\f191\"; }\n\n* .fa-caret-square-right:before {\n content: \"\\f152\"; }\n\n* .fa-caret-square-up:before {\n content: \"\\f151\"; }\n\n* .fa-caret-up:before {\n content: \"\\f0d8\"; }\n\n* .fa-carrot:before {\n content: \"\\f787\"; }\n\n* .fa-cart-arrow-down:before {\n content: \"\\f218\"; }\n\n* .fa-cart-plus:before {\n content: \"\\f217\"; }\n\n* .fa-cash-register:before {\n content: \"\\f788\"; }\n\n* .fa-cat:before {\n content: \"\\f6be\"; }\n\n* .fa-cc-amazon-pay:before {\n content: \"\\f42d\"; }\n\n* .fa-cc-amex:before {\n content: \"\\f1f3\"; }\n\n* .fa-cc-apple-pay:before {\n content: \"\\f416\"; }\n\n* .fa-cc-diners-club:before {\n content: \"\\f24c\"; }\n\n* .fa-cc-discover:before {\n content: \"\\f1f2\"; }\n\n* .fa-cc-jcb:before {\n content: \"\\f24b\"; }\n\n* .fa-cc-mastercard:before {\n content: \"\\f1f1\"; }\n\n* .fa-cc-paypal:before {\n content: \"\\f1f4\"; }\n\n* .fa-cc-stripe:before {\n content: \"\\f1f5\"; }\n\n* .fa-cc-visa:before {\n content: \"\\f1f0\"; }\n\n* .fa-centercode:before {\n content: \"\\f380\"; }\n\n* .fa-centos:before {\n content: \"\\f789\"; }\n\n* .fa-certificate:before {\n content: \"\\f0a3\"; }\n\n* .fa-chair:before {\n content: \"\\f6c0\"; }\n\n* .fa-chalkboard:before {\n content: \"\\f51b\"; }\n\n* .fa-chalkboard-teacher:before {\n content: \"\\f51c\"; }\n\n* .fa-charging-station:before {\n content: \"\\f5e7\"; }\n\n* .fa-chart-area:before {\n content: \"\\f1fe\"; }\n\n* .fa-chart-bar:before {\n content: \"\\f080\"; }\n\n* .fa-chart-line:before {\n content: \"\\f201\"; }\n\n* .fa-chart-pie:before {\n content: \"\\f200\"; }\n\n* .fa-check:before {\n content: \"\\f00c\"; }\n\n* .fa-check-circle:before {\n content: \"\\f058\"; }\n\n* .fa-check-double:before {\n content: \"\\f560\"; }\n\n* .fa-check-square:before {\n content: \"\\f14a\"; }\n\n* .fa-chess:before {\n content: \"\\f439\"; }\n\n* .fa-chess-bishop:before {\n content: \"\\f43a\"; }\n\n* .fa-chess-board:before {\n content: \"\\f43c\"; }\n\n* .fa-chess-king:before {\n content: \"\\f43f\"; }\n\n* .fa-chess-knight:before {\n content: \"\\f441\"; }\n\n* .fa-chess-pawn:before {\n content: \"\\f443\"; }\n\n* .fa-chess-queen:before {\n content: \"\\f445\"; }\n\n* .fa-chess-rook:before {\n content: \"\\f447\"; }\n\n* .fa-chevron-circle-down:before {\n content: \"\\f13a\"; }\n\n* .fa-chevron-circle-left:before {\n content: \"\\f137\"; }\n\n* .fa-chevron-circle-right:before {\n content: \"\\f138\"; }\n\n* .fa-chevron-circle-up:before {\n content: \"\\f139\"; }\n\n* .fa-chevron-down:before {\n content: \"\\f078\"; }\n\n* .fa-chevron-left:before {\n content: \"\\f053\"; }\n\n* .fa-chevron-right:before {\n content: \"\\f054\"; }\n\n* .fa-chevron-up:before {\n content: \"\\f077\"; }\n\n* .fa-child:before {\n content: \"\\f1ae\"; }\n\n* .fa-chrome:before {\n content: \"\\f268\"; }\n\n* .fa-church:before {\n content: \"\\f51d\"; }\n\n* .fa-circle:before {\n content: \"\\f111\"; }\n\n* .fa-circle-notch:before {\n content: \"\\f1ce\"; }\n\n* .fa-city:before {\n content: \"\\f64f\"; }\n\n* .fa-clipboard:before {\n content: \"\\f328\"; }\n\n* .fa-clipboard-check:before {\n content: \"\\f46c\"; }\n\n* .fa-clipboard-list:before {\n content: \"\\f46d\"; }\n\n* .fa-clock:before {\n content: \"\\f017\"; }\n\n* .fa-clone:before {\n content: \"\\f24d\"; }\n\n* .fa-closed-captioning:before {\n content: \"\\f20a\"; }\n\n* .fa-cloud:before {\n content: \"\\f0c2\"; }\n\n* .fa-cloud-download-alt:before {\n content: \"\\f381\"; }\n\n* .fa-cloud-meatball:before {\n content: \"\\f73b\"; }\n\n* .fa-cloud-moon:before {\n content: \"\\f6c3\"; }\n\n* .fa-cloud-moon-rain:before {\n content: \"\\f73c\"; }\n\n* .fa-cloud-rain:before {\n content: \"\\f73d\"; }\n\n* .fa-cloud-showers-heavy:before {\n content: \"\\f740\"; }\n\n* .fa-cloud-sun:before {\n content: \"\\f6c4\"; }\n\n* .fa-cloud-sun-rain:before {\n content: \"\\f743\"; }\n\n* .fa-cloud-upload-alt:before {\n content: \"\\f382\"; }\n\n* .fa-cloudscale:before {\n content: \"\\f383\"; }\n\n* .fa-cloudsmith:before {\n content: \"\\f384\"; }\n\n* .fa-cloudversify:before {\n content: \"\\f385\"; }\n\n* .fa-cocktail:before {\n content: \"\\f561\"; }\n\n* .fa-code:before {\n content: \"\\f121\"; }\n\n* .fa-code-branch:before {\n content: \"\\f126\"; }\n\n* .fa-codepen:before {\n content: \"\\f1cb\"; }\n\n* .fa-codiepie:before {\n content: \"\\f284\"; }\n\n* .fa-coffee:before {\n content: \"\\f0f4\"; }\n\n* .fa-cog:before {\n content: \"\\f013\"; }\n\n* .fa-cogs:before {\n content: \"\\f085\"; }\n\n* .fa-coins:before {\n content: \"\\f51e\"; }\n\n* .fa-columns:before {\n content: \"\\f0db\"; }\n\n* .fa-comment:before {\n content: \"\\f075\"; }\n\n* .fa-comment-alt:before {\n content: \"\\f27a\"; }\n\n* .fa-comment-dollar:before {\n content: \"\\f651\"; }\n\n* .fa-comment-dots:before {\n content: \"\\f4ad\"; }\n\n* .fa-comment-slash:before {\n content: \"\\f4b3\"; }\n\n* .fa-comments:before {\n content: \"\\f086\"; }\n\n* .fa-comments-dollar:before {\n content: \"\\f653\"; }\n\n* .fa-compact-disc:before {\n content: \"\\f51f\"; }\n\n* .fa-compass:before {\n content: \"\\f14e\"; }\n\n* .fa-compress:before {\n content: \"\\f066\"; }\n\n* .fa-compress-arrows-alt:before {\n content: \"\\f78c\"; }\n\n* .fa-concierge-bell:before {\n content: \"\\f562\"; }\n\n* .fa-confluence:before {\n content: \"\\f78d\"; }\n\n* .fa-connectdevelop:before {\n content: \"\\f20e\"; }\n\n* .fa-contao:before {\n content: \"\\f26d\"; }\n\n* .fa-cookie:before {\n content: \"\\f563\"; }\n\n* .fa-cookie-bite:before {\n content: \"\\f564\"; }\n\n* .fa-copy:before {\n content: \"\\f0c5\"; }\n\n* .fa-copyright:before {\n content: \"\\f1f9\"; }\n\n* .fa-couch:before {\n content: \"\\f4b8\"; }\n\n* .fa-cpanel:before {\n content: \"\\f388\"; }\n\n* .fa-creative-commons:before {\n content: \"\\f25e\"; }\n\n* .fa-creative-commons-by:before {\n content: \"\\f4e7\"; }\n\n* .fa-creative-commons-nc:before {\n content: \"\\f4e8\"; }\n\n* .fa-creative-commons-nc-eu:before {\n content: \"\\f4e9\"; }\n\n* .fa-creative-commons-nc-jp:before {\n content: \"\\f4ea\"; }\n\n* .fa-creative-commons-nd:before {\n content: \"\\f4eb\"; }\n\n* .fa-creative-commons-pd:before {\n content: \"\\f4ec\"; }\n\n* .fa-creative-commons-pd-alt:before {\n content: \"\\f4ed\"; }\n\n* .fa-creative-commons-remix:before {\n content: \"\\f4ee\"; }\n\n* .fa-creative-commons-sa:before {\n content: \"\\f4ef\"; }\n\n* .fa-creative-commons-sampling:before {\n content: \"\\f4f0\"; }\n\n* .fa-creative-commons-sampling-plus:before {\n content: \"\\f4f1\"; }\n\n* .fa-creative-commons-share:before {\n content: \"\\f4f2\"; }\n\n* .fa-creative-commons-zero:before {\n content: \"\\f4f3\"; }\n\n* .fa-credit-card:before {\n content: \"\\f09d\"; }\n\n* .fa-critical-role:before {\n content: \"\\f6c9\"; }\n\n* .fa-crop:before {\n content: \"\\f125\"; }\n\n* .fa-crop-alt:before {\n content: \"\\f565\"; }\n\n* .fa-cross:before {\n content: \"\\f654\"; }\n\n* .fa-crosshairs:before {\n content: \"\\f05b\"; }\n\n* .fa-crow:before {\n content: \"\\f520\"; }\n\n* .fa-crown:before {\n content: \"\\f521\"; }\n\n* .fa-css3:before {\n content: \"\\f13c\"; }\n\n* .fa-css3-alt:before {\n content: \"\\f38b\"; }\n\n* .fa-cube:before {\n content: \"\\f1b2\"; }\n\n* .fa-cubes:before {\n content: \"\\f1b3\"; }\n\n* .fa-cut:before {\n content: \"\\f0c4\"; }\n\n* .fa-cuttlefish:before {\n content: \"\\f38c\"; }\n\n* .fa-d-and-d:before {\n content: \"\\f38d\"; }\n\n* .fa-d-and-d-beyond:before {\n content: \"\\f6ca\"; }\n\n* .fa-dashcube:before {\n content: \"\\f210\"; }\n\n* .fa-database:before {\n content: \"\\f1c0\"; }\n\n* .fa-deaf:before {\n content: \"\\f2a4\"; }\n\n* .fa-delicious:before {\n content: \"\\f1a5\"; }\n\n* .fa-democrat:before {\n content: \"\\f747\"; }\n\n* .fa-deploydog:before {\n content: \"\\f38e\"; }\n\n* .fa-deskpro:before {\n content: \"\\f38f\"; }\n\n* .fa-desktop:before {\n content: \"\\f108\"; }\n\n* .fa-dev:before {\n content: \"\\f6cc\"; }\n\n* .fa-deviantart:before {\n content: \"\\f1bd\"; }\n\n* .fa-dharmachakra:before {\n content: \"\\f655\"; }\n\n* .fa-dhl:before {\n content: \"\\f790\"; }\n\n* .fa-diagnoses:before {\n content: \"\\f470\"; }\n\n* .fa-diaspora:before {\n content: \"\\f791\"; }\n\n* .fa-dice:before {\n content: \"\\f522\"; }\n\n* .fa-dice-d20:before {\n content: \"\\f6cf\"; }\n\n* .fa-dice-d6:before {\n content: \"\\f6d1\"; }\n\n* .fa-dice-five:before {\n content: \"\\f523\"; }\n\n* .fa-dice-four:before {\n content: \"\\f524\"; }\n\n* .fa-dice-one:before {\n content: \"\\f525\"; }\n\n* .fa-dice-six:before {\n content: \"\\f526\"; }\n\n* .fa-dice-three:before {\n content: \"\\f527\"; }\n\n* .fa-dice-two:before {\n content: \"\\f528\"; }\n\n* .fa-digg:before {\n content: \"\\f1a6\"; }\n\n* .fa-digital-ocean:before {\n content: \"\\f391\"; }\n\n* .fa-digital-tachograph:before {\n content: \"\\f566\"; }\n\n* .fa-directions:before {\n content: \"\\f5eb\"; }\n\n* .fa-discord:before {\n content: \"\\f392\"; }\n\n* .fa-discourse:before {\n content: \"\\f393\"; }\n\n* .fa-divide:before {\n content: \"\\f529\"; }\n\n* .fa-dizzy:before {\n content: \"\\f567\"; }\n\n* .fa-dna:before {\n content: \"\\f471\"; }\n\n* .fa-dochub:before {\n content: \"\\f394\"; }\n\n* .fa-docker:before {\n content: \"\\f395\"; }\n\n* .fa-dog:before {\n content: \"\\f6d3\"; }\n\n* .fa-dollar-sign:before {\n content: \"\\f155\"; }\n\n* .fa-dolly:before {\n content: \"\\f472\"; }\n\n* .fa-dolly-flatbed:before {\n content: \"\\f474\"; }\n\n* .fa-donate:before {\n content: \"\\f4b9\"; }\n\n* .fa-door-closed:before {\n content: \"\\f52a\"; }\n\n* .fa-door-open:before {\n content: \"\\f52b\"; }\n\n* .fa-dot-circle:before {\n content: \"\\f192\"; }\n\n* .fa-dove:before {\n content: \"\\f4ba\"; }\n\n* .fa-download:before {\n content: \"\\f019\"; }\n\n* .fa-draft2digital:before {\n content: \"\\f396\"; }\n\n* .fa-drafting-compass:before {\n content: \"\\f568\"; }\n\n* .fa-dragon:before {\n content: \"\\f6d5\"; }\n\n* .fa-draw-polygon:before {\n content: \"\\f5ee\"; }\n\n* .fa-dribbble:before {\n content: \"\\f17d\"; }\n\n* .fa-dribbble-square:before {\n content: \"\\f397\"; }\n\n* .fa-dropbox:before {\n content: \"\\f16b\"; }\n\n* .fa-drum:before {\n content: \"\\f569\"; }\n\n* .fa-drum-steelpan:before {\n content: \"\\f56a\"; }\n\n* .fa-drumstick-bite:before {\n content: \"\\f6d7\"; }\n\n* .fa-drupal:before {\n content: \"\\f1a9\"; }\n\n* .fa-dumbbell:before {\n content: \"\\f44b\"; }\n\n* .fa-dumpster:before {\n content: \"\\f793\"; }\n\n* .fa-dumpster-fire:before {\n content: \"\\f794\"; }\n\n* .fa-dungeon:before {\n content: \"\\f6d9\"; }\n\n* .fa-dyalog:before {\n content: \"\\f399\"; }\n\n* .fa-earlybirds:before {\n content: \"\\f39a\"; }\n\n* .fa-ebay:before {\n content: \"\\f4f4\"; }\n\n* .fa-edge:before {\n content: \"\\f282\"; }\n\n* .fa-edit:before {\n content: \"\\f044\"; }\n\n* .fa-eject:before {\n content: \"\\f052\"; }\n\n* .fa-elementor:before {\n content: \"\\f430\"; }\n\n* .fa-ellipsis-h:before {\n content: \"\\f141\"; }\n\n* .fa-ellipsis-v:before {\n content: \"\\f142\"; }\n\n* .fa-ello:before {\n content: \"\\f5f1\"; }\n\n* .fa-ember:before {\n content: \"\\f423\"; }\n\n* .fa-empire:before {\n content: \"\\f1d1\"; }\n\n* .fa-envelope:before {\n content: \"\\f0e0\"; }\n\n* .fa-envelope-open:before {\n content: \"\\f2b6\"; }\n\n* .fa-envelope-open-text:before {\n content: \"\\f658\"; }\n\n* .fa-envelope-square:before {\n content: \"\\f199\"; }\n\n* .fa-envira:before {\n content: \"\\f299\"; }\n\n* .fa-equals:before {\n content: \"\\f52c\"; }\n\n* .fa-eraser:before {\n content: \"\\f12d\"; }\n\n* .fa-erlang:before {\n content: \"\\f39d\"; }\n\n* .fa-ethereum:before {\n content: \"\\f42e\"; }\n\n* .fa-ethernet:before {\n content: \"\\f796\"; }\n\n* .fa-etsy:before {\n content: \"\\f2d7\"; }\n\n* .fa-euro-sign:before {\n content: \"\\f153\"; }\n\n* .fa-exchange-alt:before {\n content: \"\\f362\"; }\n\n* .fa-exclamation:before {\n content: \"\\f12a\"; }\n\n* .fa-exclamation-circle:before {\n content: \"\\f06a\"; }\n\n* .fa-exclamation-triangle:before {\n content: \"\\f071\"; }\n\n* .fa-expand:before {\n content: \"\\f065\"; }\n\n* .fa-expand-arrows-alt:before {\n content: \"\\f31e\"; }\n\n* .fa-expeditedssl:before {\n content: \"\\f23e\"; }\n\n* .fa-external-link-alt:before {\n content: \"\\f35d\"; }\n\n* .fa-external-link-square-alt:before {\n content: \"\\f360\"; }\n\n* .fa-eye:before {\n content: \"\\f06e\"; }\n\n* .fa-eye-dropper:before {\n content: \"\\f1fb\"; }\n\n* .fa-eye-slash:before {\n content: \"\\f070\"; }\n\n* .fa-facebook:before {\n content: \"\\f09a\"; }\n\n* .fa-facebook-f:before {\n content: \"\\f39e\"; }\n\n* .fa-facebook-messenger:before {\n content: \"\\f39f\"; }\n\n* .fa-facebook-square:before {\n content: \"\\f082\"; }\n\n* .fa-fantasy-flight-games:before {\n content: \"\\f6dc\"; }\n\n* .fa-fast-backward:before {\n content: \"\\f049\"; }\n\n* .fa-fast-forward:before {\n content: \"\\f050\"; }\n\n* .fa-fax:before {\n content: \"\\f1ac\"; }\n\n* .fa-feather:before {\n content: \"\\f52d\"; }\n\n* .fa-feather-alt:before {\n content: \"\\f56b\"; }\n\n* .fa-fedex:before {\n content: \"\\f797\"; }\n\n* .fa-fedora:before {\n content: \"\\f798\"; }\n\n* .fa-female:before {\n content: \"\\f182\"; }\n\n* .fa-fighter-jet:before {\n content: \"\\f0fb\"; }\n\n* .fa-figma:before {\n content: \"\\f799\"; }\n\n* .fa-file:before {\n content: \"\\f15b\"; }\n\n* .fa-file-alt:before {\n content: \"\\f15c\"; }\n\n* .fa-file-archive:before {\n content: \"\\f1c6\"; }\n\n* .fa-file-audio:before {\n content: \"\\f1c7\"; }\n\n* .fa-file-code:before {\n content: \"\\f1c9\"; }\n\n* .fa-file-contract:before {\n content: \"\\f56c\"; }\n\n* .fa-file-csv:before {\n content: \"\\f6dd\"; }\n\n* .fa-file-download:before {\n content: \"\\f56d\"; }\n\n* .fa-file-excel:before {\n content: \"\\f1c3\"; }\n\n* .fa-file-export:before {\n content: \"\\f56e\"; }\n\n* .fa-file-image:before {\n content: \"\\f1c5\"; }\n\n* .fa-file-import:before {\n content: \"\\f56f\"; }\n\n* .fa-file-invoice:before {\n content: \"\\f570\"; }\n\n* .fa-file-invoice-dollar:before {\n content: \"\\f571\"; }\n\n* .fa-file-medical:before {\n content: \"\\f477\"; }\n\n* .fa-file-medical-alt:before {\n content: \"\\f478\"; }\n\n* .fa-file-pdf:before {\n content: \"\\f1c1\"; }\n\n* .fa-file-powerpoint:before {\n content: \"\\f1c4\"; }\n\n* .fa-file-prescription:before {\n content: \"\\f572\"; }\n\n* .fa-file-signature:before {\n content: \"\\f573\"; }\n\n* .fa-file-upload:before {\n content: \"\\f574\"; }\n\n* .fa-file-video:before {\n content: \"\\f1c8\"; }\n\n* .fa-file-word:before {\n content: \"\\f1c2\"; }\n\n* .fa-fill:before {\n content: \"\\f575\"; }\n\n* .fa-fill-drip:before {\n content: \"\\f576\"; }\n\n* .fa-film:before {\n content: \"\\f008\"; }\n\n* .fa-filter:before {\n content: \"\\f0b0\"; }\n\n* .fa-fingerprint:before {\n content: \"\\f577\"; }\n\n* .fa-fire:before {\n content: \"\\f06d\"; }\n\n* .fa-fire-alt:before {\n content: \"\\f7e4\"; }\n\n* .fa-fire-extinguisher:before {\n content: \"\\f134\"; }\n\n* .fa-firefox:before {\n content: \"\\f269\"; }\n\n* .fa-first-aid:before {\n content: \"\\f479\"; }\n\n* .fa-first-order:before {\n content: \"\\f2b0\"; }\n\n* .fa-first-order-alt:before {\n content: \"\\f50a\"; }\n\n* .fa-firstdraft:before {\n content: \"\\f3a1\"; }\n\n* .fa-fish:before {\n content: \"\\f578\"; }\n\n* .fa-fist-raised:before {\n content: \"\\f6de\"; }\n\n* .fa-flag:before {\n content: \"\\f024\"; }\n\n* .fa-flag-checkered:before {\n content: \"\\f11e\"; }\n\n* .fa-flag-usa:before {\n content: \"\\f74d\"; }\n\n* .fa-flask:before {\n content: \"\\f0c3\"; }\n\n* .fa-flickr:before {\n content: \"\\f16e\"; }\n\n* .fa-flipboard:before {\n content: \"\\f44d\"; }\n\n* .fa-flushed:before {\n content: \"\\f579\"; }\n\n* .fa-fly:before {\n content: \"\\f417\"; }\n\n* .fa-folder:before {\n content: \"\\f07b\"; }\n\n* .fa-folder-minus:before {\n content: \"\\f65d\"; }\n\n* .fa-folder-open:before {\n content: \"\\f07c\"; }\n\n* .fa-folder-plus:before {\n content: \"\\f65e\"; }\n\n* .fa-font:before {\n content: \"\\f031\"; }\n\n* .fa-font-awesome:before {\n content: \"\\f2b4\"; }\n\n* .fa-font-awesome-alt:before {\n content: \"\\f35c\"; }\n\n* .fa-font-awesome-flag:before {\n content: \"\\f425\"; }\n\n* .fa-font-awesome-logo-full:before {\n content: \"\\f4e6\"; }\n\n* .fa-fonticons:before {\n content: \"\\f280\"; }\n\n* .fa-fonticons-fi:before {\n content: \"\\f3a2\"; }\n\n* .fa-football-ball:before {\n content: \"\\f44e\"; }\n\n* .fa-fort-awesome:before {\n content: \"\\f286\"; }\n\n* .fa-fort-awesome-alt:before {\n content: \"\\f3a3\"; }\n\n* .fa-forumbee:before {\n content: \"\\f211\"; }\n\n* .fa-forward:before {\n content: \"\\f04e\"; }\n\n* .fa-foursquare:before {\n content: \"\\f180\"; }\n\n* .fa-free-code-camp:before {\n content: \"\\f2c5\"; }\n\n* .fa-freebsd:before {\n content: \"\\f3a4\"; }\n\n* .fa-frog:before {\n content: \"\\f52e\"; }\n\n* .fa-frown:before {\n content: \"\\f119\"; }\n\n* .fa-frown-open:before {\n content: \"\\f57a\"; }\n\n* .fa-fulcrum:before {\n content: \"\\f50b\"; }\n\n* .fa-funnel-dollar:before {\n content: \"\\f662\"; }\n\n* .fa-futbol:before {\n content: \"\\f1e3\"; }\n\n* .fa-galactic-republic:before {\n content: \"\\f50c\"; }\n\n* .fa-galactic-senate:before {\n content: \"\\f50d\"; }\n\n* .fa-gamepad:before {\n content: \"\\f11b\"; }\n\n* .fa-gas-pump:before {\n content: \"\\f52f\"; }\n\n* .fa-gavel:before {\n content: \"\\f0e3\"; }\n\n* .fa-gem:before {\n content: \"\\f3a5\"; }\n\n* .fa-genderless:before {\n content: \"\\f22d\"; }\n\n* .fa-get-pocket:before {\n content: \"\\f265\"; }\n\n* .fa-gg:before {\n content: \"\\f260\"; }\n\n* .fa-gg-circle:before {\n content: \"\\f261\"; }\n\n* .fa-ghost:before {\n content: \"\\f6e2\"; }\n\n* .fa-gift:before {\n content: \"\\f06b\"; }\n\n* .fa-gifts:before {\n content: \"\\f79c\"; }\n\n* .fa-git:before {\n content: \"\\f1d3\"; }\n\n* .fa-git-square:before {\n content: \"\\f1d2\"; }\n\n* .fa-github:before {\n content: \"\\f09b\"; }\n\n* .fa-github-alt:before {\n content: \"\\f113\"; }\n\n* .fa-github-square:before {\n content: \"\\f092\"; }\n\n* .fa-gitkraken:before {\n content: \"\\f3a6\"; }\n\n* .fa-gitlab:before {\n content: \"\\f296\"; }\n\n* .fa-gitter:before {\n content: \"\\f426\"; }\n\n* .fa-glass-cheers:before {\n content: \"\\f79f\"; }\n\n* .fa-glass-martini:before {\n content: \"\\f000\"; }\n\n* .fa-glass-martini-alt:before {\n content: \"\\f57b\"; }\n\n* .fa-glass-whiskey:before {\n content: \"\\f7a0\"; }\n\n* .fa-glasses:before {\n content: \"\\f530\"; }\n\n* .fa-glide:before {\n content: \"\\f2a5\"; }\n\n* .fa-glide-g:before {\n content: \"\\f2a6\"; }\n\n* .fa-globe:before {\n content: \"\\f0ac\"; }\n\n* .fa-globe-africa:before {\n content: \"\\f57c\"; }\n\n* .fa-globe-americas:before {\n content: \"\\f57d\"; }\n\n* .fa-globe-asia:before {\n content: \"\\f57e\"; }\n\n* .fa-globe-europe:before {\n content: \"\\f7a2\"; }\n\n* .fa-gofore:before {\n content: \"\\f3a7\"; }\n\n* .fa-golf-ball:before {\n content: \"\\f450\"; }\n\n* .fa-goodreads:before {\n content: \"\\f3a8\"; }\n\n* .fa-goodreads-g:before {\n content: \"\\f3a9\"; }\n\n* .fa-google:before {\n content: \"\\f1a0\"; }\n\n* .fa-google-drive:before {\n content: \"\\f3aa\"; }\n\n* .fa-google-play:before {\n content: \"\\f3ab\"; }\n\n* .fa-google-plus:before {\n content: \"\\f2b3\"; }\n\n* .fa-google-plus-g:before {\n content: \"\\f0d5\"; }\n\n* .fa-google-plus-square:before {\n content: \"\\f0d4\"; }\n\n* .fa-google-wallet:before {\n content: \"\\f1ee\"; }\n\n* .fa-gopuram:before {\n content: \"\\f664\"; }\n\n* .fa-graduation-cap:before {\n content: \"\\f19d\"; }\n\n* .fa-gratipay:before {\n content: \"\\f184\"; }\n\n* .fa-grav:before {\n content: \"\\f2d6\"; }\n\n* .fa-greater-than:before {\n content: \"\\f531\"; }\n\n* .fa-greater-than-equal:before {\n content: \"\\f532\"; }\n\n* .fa-grimace:before {\n content: \"\\f57f\"; }\n\n* .fa-grin:before {\n content: \"\\f580\"; }\n\n* .fa-grin-alt:before {\n content: \"\\f581\"; }\n\n* .fa-grin-beam:before {\n content: \"\\f582\"; }\n\n* .fa-grin-beam-sweat:before {\n content: \"\\f583\"; }\n\n* .fa-grin-hearts:before {\n content: \"\\f584\"; }\n\n* .fa-grin-squint:before {\n content: \"\\f585\"; }\n\n* .fa-grin-squint-tears:before {\n content: \"\\f586\"; }\n\n* .fa-grin-stars:before {\n content: \"\\f587\"; }\n\n* .fa-grin-tears:before {\n content: \"\\f588\"; }\n\n* .fa-grin-tongue:before {\n content: \"\\f589\"; }\n\n* .fa-grin-tongue-squint:before {\n content: \"\\f58a\"; }\n\n* .fa-grin-tongue-wink:before {\n content: \"\\f58b\"; }\n\n* .fa-grin-wink:before {\n content: \"\\f58c\"; }\n\n* .fa-grip-horizontal:before {\n content: \"\\f58d\"; }\n\n* .fa-grip-lines:before {\n content: \"\\f7a4\"; }\n\n* .fa-grip-lines-vertical:before {\n content: \"\\f7a5\"; }\n\n* .fa-grip-vertical:before {\n content: \"\\f58e\"; }\n\n* .fa-gripfire:before {\n content: \"\\f3ac\"; }\n\n* .fa-grunt:before {\n content: \"\\f3ad\"; }\n\n* .fa-guitar:before {\n content: \"\\f7a6\"; }\n\n* .fa-gulp:before {\n content: \"\\f3ae\"; }\n\n* .fa-h-square:before {\n content: \"\\f0fd\"; }\n\n* .fa-hacker-news:before {\n content: \"\\f1d4\"; }\n\n* .fa-hacker-news-square:before {\n content: \"\\f3af\"; }\n\n* .fa-hackerrank:before {\n content: \"\\f5f7\"; }\n\n* .fa-hammer:before {\n content: \"\\f6e3\"; }\n\n* .fa-hamsa:before {\n content: \"\\f665\"; }\n\n* .fa-hand-holding:before {\n content: \"\\f4bd\"; }\n\n* .fa-hand-holding-heart:before {\n content: \"\\f4be\"; }\n\n* .fa-hand-holding-usd:before {\n content: \"\\f4c0\"; }\n\n* .fa-hand-lizard:before {\n content: \"\\f258\"; }\n\n* .fa-hand-paper:before {\n content: \"\\f256\"; }\n\n* .fa-hand-peace:before {\n content: \"\\f25b\"; }\n\n* .fa-hand-point-down:before {\n content: \"\\f0a7\"; }\n\n* .fa-hand-point-left:before {\n content: \"\\f0a5\"; }\n\n* .fa-hand-point-right:before {\n content: \"\\f0a4\"; }\n\n* .fa-hand-point-up:before {\n content: \"\\f0a6\"; }\n\n* .fa-hand-pointer:before {\n content: \"\\f25a\"; }\n\n* .fa-hand-rock:before {\n content: \"\\f255\"; }\n\n* .fa-hand-scissors:before {\n content: \"\\f257\"; }\n\n* .fa-hand-spock:before {\n content: \"\\f259\"; }\n\n* .fa-hands:before {\n content: \"\\f4c2\"; }\n\n* .fa-hands-helping:before {\n content: \"\\f4c4\"; }\n\n* .fa-handshake:before {\n content: \"\\f2b5\"; }\n\n* .fa-hanukiah:before {\n content: \"\\f6e6\"; }\n\n* .fa-hashtag:before {\n content: \"\\f292\"; }\n\n* .fa-hat-wizard:before {\n content: \"\\f6e8\"; }\n\n* .fa-haykal:before {\n content: \"\\f666\"; }\n\n* .fa-hdd:before {\n content: \"\\f0a0\"; }\n\n* .fa-heading:before {\n content: \"\\f1dc\"; }\n\n* .fa-headphones:before {\n content: \"\\f025\"; }\n\n* .fa-headphones-alt:before {\n content: \"\\f58f\"; }\n\n* .fa-headset:before {\n content: \"\\f590\"; }\n\n* .fa-heart:before {\n content: \"\\f004\"; }\n\n* .fa-heart-broken:before {\n content: \"\\f7a9\"; }\n\n* .fa-heartbeat:before {\n content: \"\\f21e\"; }\n\n* .fa-helicopter:before {\n content: \"\\f533\"; }\n\n* .fa-highlighter:before {\n content: \"\\f591\"; }\n\n* .fa-hiking:before {\n content: \"\\f6ec\"; }\n\n* .fa-hippo:before {\n content: \"\\f6ed\"; }\n\n* .fa-hips:before {\n content: \"\\f452\"; }\n\n* .fa-hire-a-helper:before {\n content: \"\\f3b0\"; }\n\n* .fa-history:before {\n content: \"\\f1da\"; }\n\n* .fa-hockey-puck:before {\n content: \"\\f453\"; }\n\n* .fa-holly-berry:before {\n content: \"\\f7aa\"; }\n\n* .fa-home:before {\n content: \"\\f015\"; }\n\n* .fa-hooli:before {\n content: \"\\f427\"; }\n\n* .fa-hornbill:before {\n content: \"\\f592\"; }\n\n* .fa-horse:before {\n content: \"\\f6f0\"; }\n\n* .fa-horse-head:before {\n content: \"\\f7ab\"; }\n\n* .fa-hospital:before {\n content: \"\\f0f8\"; }\n\n* .fa-hospital-alt:before {\n content: \"\\f47d\"; }\n\n* .fa-hospital-symbol:before {\n content: \"\\f47e\"; }\n\n* .fa-hot-tub:before {\n content: \"\\f593\"; }\n\n* .fa-hotel:before {\n content: \"\\f594\"; }\n\n* .fa-hotjar:before {\n content: \"\\f3b1\"; }\n\n* .fa-hourglass:before {\n content: \"\\f254\"; }\n\n* .fa-hourglass-end:before {\n content: \"\\f253\"; }\n\n* .fa-hourglass-half:before {\n content: \"\\f252\"; }\n\n* .fa-hourglass-start:before {\n content: \"\\f251\"; }\n\n* .fa-house-damage:before {\n content: \"\\f6f1\"; }\n\n* .fa-houzz:before {\n content: \"\\f27c\"; }\n\n* .fa-hryvnia:before {\n content: \"\\f6f2\"; }\n\n* .fa-html5:before {\n content: \"\\f13b\"; }\n\n* .fa-hubspot:before {\n content: \"\\f3b2\"; }\n\n* .fa-i-cursor:before {\n content: \"\\f246\"; }\n\n* .fa-icicles:before {\n content: \"\\f7ad\"; }\n\n* .fa-id-badge:before {\n content: \"\\f2c1\"; }\n\n* .fa-id-card:before {\n content: \"\\f2c2\"; }\n\n* .fa-id-card-alt:before {\n content: \"\\f47f\"; }\n\n* .fa-igloo:before {\n content: \"\\f7ae\"; }\n\n* .fa-image:before {\n content: \"\\f03e\"; }\n\n* .fa-images:before {\n content: \"\\f302\"; }\n\n* .fa-imdb:before {\n content: \"\\f2d8\"; }\n\n* .fa-inbox:before {\n content: \"\\f01c\"; }\n\n* .fa-indent:before {\n content: \"\\f03c\"; }\n\n* .fa-industry:before {\n content: \"\\f275\"; }\n\n* .fa-infinity:before {\n content: \"\\f534\"; }\n\n* .fa-info:before {\n content: \"\\f129\"; }\n\n* .fa-info-circle:before {\n content: \"\\f05a\"; }\n\n* .fa-instagram:before {\n content: \"\\f16d\"; }\n\n* .fa-intercom:before {\n content: \"\\f7af\"; }\n\n* .fa-internet-explorer:before {\n content: \"\\f26b\"; }\n\n* .fa-invision:before {\n content: \"\\f7b0\"; }\n\n* .fa-ioxhost:before {\n content: \"\\f208\"; }\n\n* .fa-italic:before {\n content: \"\\f033\"; }\n\n* .fa-itunes:before {\n content: \"\\f3b4\"; }\n\n* .fa-itunes-note:before {\n content: \"\\f3b5\"; }\n\n* .fa-java:before {\n content: \"\\f4e4\"; }\n\n* .fa-jedi:before {\n content: \"\\f669\"; }\n\n* .fa-jedi-order:before {\n content: \"\\f50e\"; }\n\n* .fa-jenkins:before {\n content: \"\\f3b6\"; }\n\n* .fa-jira:before {\n content: \"\\f7b1\"; }\n\n* .fa-joget:before {\n content: \"\\f3b7\"; }\n\n* .fa-joint:before {\n content: \"\\f595\"; }\n\n* .fa-joomla:before {\n content: \"\\f1aa\"; }\n\n* .fa-journal-whills:before {\n content: \"\\f66a\"; }\n\n* .fa-js:before {\n content: \"\\f3b8\"; }\n\n* .fa-js-square:before {\n content: \"\\f3b9\"; }\n\n* .fa-jsfiddle:before {\n content: \"\\f1cc\"; }\n\n* .fa-kaaba:before {\n content: \"\\f66b\"; }\n\n* .fa-kaggle:before {\n content: \"\\f5fa\"; }\n\n* .fa-key:before {\n content: \"\\f084\"; }\n\n* .fa-keybase:before {\n content: \"\\f4f5\"; }\n\n* .fa-keyboard:before {\n content: \"\\f11c\"; }\n\n* .fa-keycdn:before {\n content: \"\\f3ba\"; }\n\n* .fa-khanda:before {\n content: \"\\f66d\"; }\n\n* .fa-kickstarter:before {\n content: \"\\f3bb\"; }\n\n* .fa-kickstarter-k:before {\n content: \"\\f3bc\"; }\n\n* .fa-kiss:before {\n content: \"\\f596\"; }\n\n* .fa-kiss-beam:before {\n content: \"\\f597\"; }\n\n* .fa-kiss-wink-heart:before {\n content: \"\\f598\"; }\n\n* .fa-kiwi-bird:before {\n content: \"\\f535\"; }\n\n* .fa-korvue:before {\n content: \"\\f42f\"; }\n\n* .fa-landmark:before {\n content: \"\\f66f\"; }\n\n* .fa-language:before {\n content: \"\\f1ab\"; }\n\n* .fa-laptop:before {\n content: \"\\f109\"; }\n\n* .fa-laptop-code:before {\n content: \"\\f5fc\"; }\n\n* .fa-laravel:before {\n content: \"\\f3bd\"; }\n\n* .fa-lastfm:before {\n content: \"\\f202\"; }\n\n* .fa-lastfm-square:before {\n content: \"\\f203\"; }\n\n* .fa-laugh:before {\n content: \"\\f599\"; }\n\n* .fa-laugh-beam:before {\n content: \"\\f59a\"; }\n\n* .fa-laugh-squint:before {\n content: \"\\f59b\"; }\n\n* .fa-laugh-wink:before {\n content: \"\\f59c\"; }\n\n* .fa-layer-group:before {\n content: \"\\f5fd\"; }\n\n* .fa-leaf:before {\n content: \"\\f06c\"; }\n\n* .fa-leanpub:before {\n content: \"\\f212\"; }\n\n* .fa-lemon:before {\n content: \"\\f094\"; }\n\n* .fa-less:before {\n content: \"\\f41d\"; }\n\n* .fa-less-than:before {\n content: \"\\f536\"; }\n\n* .fa-less-than-equal:before {\n content: \"\\f537\"; }\n\n* .fa-level-down-alt:before {\n content: \"\\f3be\"; }\n\n* .fa-level-up-alt:before {\n content: \"\\f3bf\"; }\n\n* .fa-life-ring:before {\n content: \"\\f1cd\"; }\n\n* .fa-lightbulb:before {\n content: \"\\f0eb\"; }\n\n* .fa-line:before {\n content: \"\\f3c0\"; }\n\n* .fa-link:before {\n content: \"\\f0c1\"; }\n\n* .fa-linkedin:before {\n content: \"\\f08c\"; }\n\n* .fa-linkedin-in:before {\n content: \"\\f0e1\"; }\n\n* .fa-linode:before {\n content: \"\\f2b8\"; }\n\n* .fa-linux:before {\n content: \"\\f17c\"; }\n\n* .fa-lira-sign:before {\n content: \"\\f195\"; }\n\n* .fa-list:before {\n content: \"\\f03a\"; }\n\n* .fa-list-alt:before {\n content: \"\\f022\"; }\n\n* .fa-list-ol:before {\n content: \"\\f0cb\"; }\n\n* .fa-list-ul:before {\n content: \"\\f0ca\"; }\n\n* .fa-location-arrow:before {\n content: \"\\f124\"; }\n\n* .fa-lock:before {\n content: \"\\f023\"; }\n\n* .fa-lock-open:before {\n content: \"\\f3c1\"; }\n\n* .fa-long-arrow-alt-down:before {\n content: \"\\f309\"; }\n\n* .fa-long-arrow-alt-left:before {\n content: \"\\f30a\"; }\n\n* .fa-long-arrow-alt-right:before {\n content: \"\\f30b\"; }\n\n* .fa-long-arrow-alt-up:before {\n content: \"\\f30c\"; }\n\n* .fa-low-vision:before {\n content: \"\\f2a8\"; }\n\n* .fa-luggage-cart:before {\n content: \"\\f59d\"; }\n\n* .fa-lyft:before {\n content: \"\\f3c3\"; }\n\n* .fa-magento:before {\n content: \"\\f3c4\"; }\n\n* .fa-magic:before {\n content: \"\\f0d0\"; }\n\n* .fa-magnet:before {\n content: \"\\f076\"; }\n\n* .fa-mail-bulk:before {\n content: \"\\f674\"; }\n\n* .fa-mailchimp:before {\n content: \"\\f59e\"; }\n\n* .fa-male:before {\n content: \"\\f183\"; }\n\n* .fa-mandalorian:before {\n content: \"\\f50f\"; }\n\n* .fa-map:before {\n content: \"\\f279\"; }\n\n* .fa-map-marked:before {\n content: \"\\f59f\"; }\n\n* .fa-map-marked-alt:before {\n content: \"\\f5a0\"; }\n\n* .fa-map-marker:before {\n content: \"\\f041\"; }\n\n* .fa-map-marker-alt:before {\n content: \"\\f3c5\"; }\n\n* .fa-map-pin:before {\n content: \"\\f276\"; }\n\n* .fa-map-signs:before {\n content: \"\\f277\"; }\n\n* .fa-markdown:before {\n content: \"\\f60f\"; }\n\n* .fa-marker:before {\n content: \"\\f5a1\"; }\n\n* .fa-mars:before {\n content: \"\\f222\"; }\n\n* .fa-mars-double:before {\n content: \"\\f227\"; }\n\n* .fa-mars-stroke:before {\n content: \"\\f229\"; }\n\n* .fa-mars-stroke-h:before {\n content: \"\\f22b\"; }\n\n* .fa-mars-stroke-v:before {\n content: \"\\f22a\"; }\n\n* .fa-mask:before {\n content: \"\\f6fa\"; }\n\n* .fa-mastodon:before {\n content: \"\\f4f6\"; }\n\n* .fa-maxcdn:before {\n content: \"\\f136\"; }\n\n* .fa-medal:before {\n content: \"\\f5a2\"; }\n\n* .fa-medapps:before {\n content: \"\\f3c6\"; }\n\n* .fa-medium:before {\n content: \"\\f23a\"; }\n\n* .fa-medium-m:before {\n content: \"\\f3c7\"; }\n\n* .fa-medkit:before {\n content: \"\\f0fa\"; }\n\n* .fa-medrt:before {\n content: \"\\f3c8\"; }\n\n* .fa-meetup:before {\n content: \"\\f2e0\"; }\n\n* .fa-megaport:before {\n content: \"\\f5a3\"; }\n\n* .fa-meh:before {\n content: \"\\f11a\"; }\n\n* .fa-meh-blank:before {\n content: \"\\f5a4\"; }\n\n* .fa-meh-rolling-eyes:before {\n content: \"\\f5a5\"; }\n\n* .fa-memory:before {\n content: \"\\f538\"; }\n\n* .fa-mendeley:before {\n content: \"\\f7b3\"; }\n\n* .fa-menorah:before {\n content: \"\\f676\"; }\n\n* .fa-mercury:before {\n content: \"\\f223\"; }\n\n* .fa-meteor:before {\n content: \"\\f753\"; }\n\n* .fa-microchip:before {\n content: \"\\f2db\"; }\n\n* .fa-microphone:before {\n content: \"\\f130\"; }\n\n* .fa-microphone-alt:before {\n content: \"\\f3c9\"; }\n\n* .fa-microphone-alt-slash:before {\n content: \"\\f539\"; }\n\n* .fa-microphone-slash:before {\n content: \"\\f131\"; }\n\n* .fa-microscope:before {\n content: \"\\f610\"; }\n\n* .fa-microsoft:before {\n content: \"\\f3ca\"; }\n\n* .fa-minus:before {\n content: \"\\f068\"; }\n\n* .fa-minus-circle:before {\n content: \"\\f056\"; }\n\n* .fa-minus-square:before {\n content: \"\\f146\"; }\n\n* .fa-mitten:before {\n content: \"\\f7b5\"; }\n\n* .fa-mix:before {\n content: \"\\f3cb\"; }\n\n* .fa-mixcloud:before {\n content: \"\\f289\"; }\n\n* .fa-mizuni:before {\n content: \"\\f3cc\"; }\n\n* .fa-mobile:before {\n content: \"\\f10b\"; }\n\n* .fa-mobile-alt:before {\n content: \"\\f3cd\"; }\n\n* .fa-modx:before {\n content: \"\\f285\"; }\n\n* .fa-monero:before {\n content: \"\\f3d0\"; }\n\n* .fa-money-bill:before {\n content: \"\\f0d6\"; }\n\n* .fa-money-bill-alt:before {\n content: \"\\f3d1\"; }\n\n* .fa-money-bill-wave:before {\n content: \"\\f53a\"; }\n\n* .fa-money-bill-wave-alt:before {\n content: \"\\f53b\"; }\n\n* .fa-money-check:before {\n content: \"\\f53c\"; }\n\n* .fa-money-check-alt:before {\n content: \"\\f53d\"; }\n\n* .fa-monument:before {\n content: \"\\f5a6\"; }\n\n* .fa-moon:before {\n content: \"\\f186\"; }\n\n* .fa-mortar-pestle:before {\n content: \"\\f5a7\"; }\n\n* .fa-mosque:before {\n content: \"\\f678\"; }\n\n* .fa-motorcycle:before {\n content: \"\\f21c\"; }\n\n* .fa-mountain:before {\n content: \"\\f6fc\"; }\n\n* .fa-mouse-pointer:before {\n content: \"\\f245\"; }\n\n* .fa-mug-hot:before {\n content: \"\\f7b6\"; }\n\n* .fa-music:before {\n content: \"\\f001\"; }\n\n* .fa-napster:before {\n content: \"\\f3d2\"; }\n\n* .fa-neos:before {\n content: \"\\f612\"; }\n\n* .fa-network-wired:before {\n content: \"\\f6ff\"; }\n\n* .fa-neuter:before {\n content: \"\\f22c\"; }\n\n* .fa-newspaper:before {\n content: \"\\f1ea\"; }\n\n* .fa-nimblr:before {\n content: \"\\f5a8\"; }\n\n* .fa-nintendo-switch:before {\n content: \"\\f418\"; }\n\n* .fa-node:before {\n content: \"\\f419\"; }\n\n* .fa-node-js:before {\n content: \"\\f3d3\"; }\n\n* .fa-not-equal:before {\n content: \"\\f53e\"; }\n\n* .fa-notes-medical:before {\n content: \"\\f481\"; }\n\n* .fa-npm:before {\n content: \"\\f3d4\"; }\n\n* .fa-ns8:before {\n content: \"\\f3d5\"; }\n\n* .fa-nutritionix:before {\n content: \"\\f3d6\"; }\n\n* .fa-object-group:before {\n content: \"\\f247\"; }\n\n* .fa-object-ungroup:before {\n content: \"\\f248\"; }\n\n* .fa-odnoklassniki:before {\n content: \"\\f263\"; }\n\n* .fa-odnoklassniki-square:before {\n content: \"\\f264\"; }\n\n* .fa-oil-can:before {\n content: \"\\f613\"; }\n\n* .fa-old-republic:before {\n content: \"\\f510\"; }\n\n* .fa-om:before {\n content: \"\\f679\"; }\n\n* .fa-opencart:before {\n content: \"\\f23d\"; }\n\n* .fa-openid:before {\n content: \"\\f19b\"; }\n\n* .fa-opera:before {\n content: \"\\f26a\"; }\n\n* .fa-optin-monster:before {\n content: \"\\f23c\"; }\n\n* .fa-osi:before {\n content: \"\\f41a\"; }\n\n* .fa-otter:before {\n content: \"\\f700\"; }\n\n* .fa-outdent:before {\n content: \"\\f03b\"; }\n\n* .fa-page4:before {\n content: \"\\f3d7\"; }\n\n* .fa-pagelines:before {\n content: \"\\f18c\"; }\n\n* .fa-paint-brush:before {\n content: \"\\f1fc\"; }\n\n* .fa-paint-roller:before {\n content: \"\\f5aa\"; }\n\n* .fa-palette:before {\n content: \"\\f53f\"; }\n\n* .fa-palfed:before {\n content: \"\\f3d8\"; }\n\n* .fa-pallet:before {\n content: \"\\f482\"; }\n\n* .fa-paper-plane:before {\n content: \"\\f1d8\"; }\n\n* .fa-paperclip:before {\n content: \"\\f0c6\"; }\n\n* .fa-parachute-box:before {\n content: \"\\f4cd\"; }\n\n* .fa-paragraph:before {\n content: \"\\f1dd\"; }\n\n* .fa-parking:before {\n content: \"\\f540\"; }\n\n* .fa-passport:before {\n content: \"\\f5ab\"; }\n\n* .fa-pastafarianism:before {\n content: \"\\f67b\"; }\n\n* .fa-paste:before {\n content: \"\\f0ea\"; }\n\n* .fa-patreon:before {\n content: \"\\f3d9\"; }\n\n* .fa-pause:before {\n content: \"\\f04c\"; }\n\n* .fa-pause-circle:before {\n content: \"\\f28b\"; }\n\n* .fa-paw:before {\n content: \"\\f1b0\"; }\n\n* .fa-paypal:before {\n content: \"\\f1ed\"; }\n\n* .fa-peace:before {\n content: \"\\f67c\"; }\n\n* .fa-pen:before {\n content: \"\\f304\"; }\n\n* .fa-pen-alt:before {\n content: \"\\f305\"; }\n\n* .fa-pen-fancy:before {\n content: \"\\f5ac\"; }\n\n* .fa-pen-nib:before {\n content: \"\\f5ad\"; }\n\n* .fa-pen-square:before {\n content: \"\\f14b\"; }\n\n* .fa-pencil-alt:before {\n content: \"\\f303\"; }\n\n* .fa-pencil-ruler:before {\n content: \"\\f5ae\"; }\n\n* .fa-penny-arcade:before {\n content: \"\\f704\"; }\n\n* .fa-people-carry:before {\n content: \"\\f4ce\"; }\n\n* .fa-percent:before {\n content: \"\\f295\"; }\n\n* .fa-percentage:before {\n content: \"\\f541\"; }\n\n* .fa-periscope:before {\n content: \"\\f3da\"; }\n\n* .fa-person-booth:before {\n content: \"\\f756\"; }\n\n* .fa-phabricator:before {\n content: \"\\f3db\"; }\n\n* .fa-phoenix-framework:before {\n content: \"\\f3dc\"; }\n\n* .fa-phoenix-squadron:before {\n content: \"\\f511\"; }\n\n* .fa-phone:before {\n content: \"\\f095\"; }\n\n* .fa-phone-slash:before {\n content: \"\\f3dd\"; }\n\n* .fa-phone-square:before {\n content: \"\\f098\"; }\n\n* .fa-phone-volume:before {\n content: \"\\f2a0\"; }\n\n* .fa-php:before {\n content: \"\\f457\"; }\n\n* .fa-pied-piper:before {\n content: \"\\f2ae\"; }\n\n* .fa-pied-piper-alt:before {\n content: \"\\f1a8\"; }\n\n* .fa-pied-piper-hat:before {\n content: \"\\f4e5\"; }\n\n* .fa-pied-piper-pp:before {\n content: \"\\f1a7\"; }\n\n* .fa-piggy-bank:before {\n content: \"\\f4d3\"; }\n\n* .fa-pills:before {\n content: \"\\f484\"; }\n\n* .fa-pinterest:before {\n content: \"\\f0d2\"; }\n\n* .fa-pinterest-p:before {\n content: \"\\f231\"; }\n\n* .fa-pinterest-square:before {\n content: \"\\f0d3\"; }\n\n* .fa-place-of-worship:before {\n content: \"\\f67f\"; }\n\n* .fa-plane:before {\n content: \"\\f072\"; }\n\n* .fa-plane-arrival:before {\n content: \"\\f5af\"; }\n\n* .fa-plane-departure:before {\n content: \"\\f5b0\"; }\n\n* .fa-play:before {\n content: \"\\f04b\"; }\n\n* .fa-play-circle:before {\n content: \"\\f144\"; }\n\n* .fa-playstation:before {\n content: \"\\f3df\"; }\n\n* .fa-plug:before {\n content: \"\\f1e6\"; }\n\n* .fa-plus:before {\n content: \"\\f067\"; }\n\n* .fa-plus-circle:before {\n content: \"\\f055\"; }\n\n* .fa-plus-square:before {\n content: \"\\f0fe\"; }\n\n* .fa-podcast:before {\n content: \"\\f2ce\"; }\n\n* .fa-poll:before {\n content: \"\\f681\"; }\n\n* .fa-poll-h:before {\n content: \"\\f682\"; }\n\n* .fa-poo:before {\n content: \"\\f2fe\"; }\n\n* .fa-poo-storm:before {\n content: \"\\f75a\"; }\n\n* .fa-poop:before {\n content: \"\\f619\"; }\n\n* .fa-portrait:before {\n content: \"\\f3e0\"; }\n\n* .fa-pound-sign:before {\n content: \"\\f154\"; }\n\n* .fa-power-off:before {\n content: \"\\f011\"; }\n\n* .fa-pray:before {\n content: \"\\f683\"; }\n\n* .fa-praying-hands:before {\n content: \"\\f684\"; }\n\n* .fa-prescription:before {\n content: \"\\f5b1\"; }\n\n* .fa-prescription-bottle:before {\n content: \"\\f485\"; }\n\n* .fa-prescription-bottle-alt:before {\n content: \"\\f486\"; }\n\n* .fa-print:before {\n content: \"\\f02f\"; }\n\n* .fa-procedures:before {\n content: \"\\f487\"; }\n\n* .fa-product-hunt:before {\n content: \"\\f288\"; }\n\n* .fa-project-diagram:before {\n content: \"\\f542\"; }\n\n* .fa-pushed:before {\n content: \"\\f3e1\"; }\n\n* .fa-puzzle-piece:before {\n content: \"\\f12e\"; }\n\n* .fa-python:before {\n content: \"\\f3e2\"; }\n\n* .fa-qq:before {\n content: \"\\f1d6\"; }\n\n* .fa-qrcode:before {\n content: \"\\f029\"; }\n\n* .fa-question:before {\n content: \"\\f128\"; }\n\n* .fa-question-circle:before {\n content: \"\\f059\"; }\n\n* .fa-quidditch:before {\n content: \"\\f458\"; }\n\n* .fa-quinscape:before {\n content: \"\\f459\"; }\n\n* .fa-quora:before {\n content: \"\\f2c4\"; }\n\n* .fa-quote-left:before {\n content: \"\\f10d\"; }\n\n* .fa-quote-right:before {\n content: \"\\f10e\"; }\n\n* .fa-quran:before {\n content: \"\\f687\"; }\n\n* .fa-r-project:before {\n content: \"\\f4f7\"; }\n\n* .fa-radiation:before {\n content: \"\\f7b9\"; }\n\n* .fa-radiation-alt:before {\n content: \"\\f7ba\"; }\n\n* .fa-rainbow:before {\n content: \"\\f75b\"; }\n\n* .fa-random:before {\n content: \"\\f074\"; }\n\n* .fa-raspberry-pi:before {\n content: \"\\f7bb\"; }\n\n* .fa-ravelry:before {\n content: \"\\f2d9\"; }\n\n* .fa-react:before {\n content: \"\\f41b\"; }\n\n* .fa-reacteurope:before {\n content: \"\\f75d\"; }\n\n* .fa-readme:before {\n content: \"\\f4d5\"; }\n\n* .fa-rebel:before {\n content: \"\\f1d0\"; }\n\n* .fa-receipt:before {\n content: \"\\f543\"; }\n\n* .fa-recycle:before {\n content: \"\\f1b8\"; }\n\n* .fa-red-river:before {\n content: \"\\f3e3\"; }\n\n* .fa-reddit:before {\n content: \"\\f1a1\"; }\n\n* .fa-reddit-alien:before {\n content: \"\\f281\"; }\n\n* .fa-reddit-square:before {\n content: \"\\f1a2\"; }\n\n* .fa-redhat:before {\n content: \"\\f7bc\"; }\n\n* .fa-redo:before {\n content: \"\\f01e\"; }\n\n* .fa-redo-alt:before {\n content: \"\\f2f9\"; }\n\n* .fa-registered:before {\n content: \"\\f25d\"; }\n\n* .fa-renren:before {\n content: \"\\f18b\"; }\n\n* .fa-reply:before {\n content: \"\\f3e5\"; }\n\n* .fa-reply-all:before {\n content: \"\\f122\"; }\n\n* .fa-replyd:before {\n content: \"\\f3e6\"; }\n\n* .fa-republican:before {\n content: \"\\f75e\"; }\n\n* .fa-researchgate:before {\n content: \"\\f4f8\"; }\n\n* .fa-resolving:before {\n content: \"\\f3e7\"; }\n\n* .fa-restroom:before {\n content: \"\\f7bd\"; }\n\n* .fa-retweet:before {\n content: \"\\f079\"; }\n\n* .fa-rev:before {\n content: \"\\f5b2\"; }\n\n* .fa-ribbon:before {\n content: \"\\f4d6\"; }\n\n* .fa-ring:before {\n content: \"\\f70b\"; }\n\n* .fa-road:before {\n content: \"\\f018\"; }\n\n* .fa-robot:before {\n content: \"\\f544\"; }\n\n* .fa-rocket:before {\n content: \"\\f135\"; }\n\n* .fa-rocketchat:before {\n content: \"\\f3e8\"; }\n\n* .fa-rockrms:before {\n content: \"\\f3e9\"; }\n\n* .fa-route:before {\n content: \"\\f4d7\"; }\n\n* .fa-rss:before {\n content: \"\\f09e\"; }\n\n* .fa-rss-square:before {\n content: \"\\f143\"; }\n\n* .fa-ruble-sign:before {\n content: \"\\f158\"; }\n\n* .fa-ruler:before {\n content: \"\\f545\"; }\n\n* .fa-ruler-combined:before {\n content: \"\\f546\"; }\n\n* .fa-ruler-horizontal:before {\n content: \"\\f547\"; }\n\n* .fa-ruler-vertical:before {\n content: \"\\f548\"; }\n\n* .fa-running:before {\n content: \"\\f70c\"; }\n\n* .fa-rupee-sign:before {\n content: \"\\f156\"; }\n\n* .fa-sad-cry:before {\n content: \"\\f5b3\"; }\n\n* .fa-sad-tear:before {\n content: \"\\f5b4\"; }\n\n* .fa-safari:before {\n content: \"\\f267\"; }\n\n* .fa-sass:before {\n content: \"\\f41e\"; }\n\n* .fa-satellite:before {\n content: \"\\f7bf\"; }\n\n* .fa-satellite-dish:before {\n content: \"\\f7c0\"; }\n\n* .fa-save:before {\n content: \"\\f0c7\"; }\n\n* .fa-schlix:before {\n content: \"\\f3ea\"; }\n\n* .fa-school:before {\n content: \"\\f549\"; }\n\n* .fa-screwdriver:before {\n content: \"\\f54a\"; }\n\n* .fa-scribd:before {\n content: \"\\f28a\"; }\n\n* .fa-scroll:before {\n content: \"\\f70e\"; }\n\n* .fa-sd-card:before {\n content: \"\\f7c2\"; }\n\n* .fa-search:before {\n content: \"\\f002\"; }\n\n* .fa-search-dollar:before {\n content: \"\\f688\"; }\n\n* .fa-search-location:before {\n content: \"\\f689\"; }\n\n* .fa-search-minus:before {\n content: \"\\f010\"; }\n\n* .fa-search-plus:before {\n content: \"\\f00e\"; }\n\n* .fa-searchengin:before {\n content: \"\\f3eb\"; }\n\n* .fa-seedling:before {\n content: \"\\f4d8\"; }\n\n* .fa-sellcast:before {\n content: \"\\f2da\"; }\n\n* .fa-sellsy:before {\n content: \"\\f213\"; }\n\n* .fa-server:before {\n content: \"\\f233\"; }\n\n* .fa-servicestack:before {\n content: \"\\f3ec\"; }\n\n* .fa-shapes:before {\n content: \"\\f61f\"; }\n\n* .fa-share:before {\n content: \"\\f064\"; }\n\n* .fa-share-alt:before {\n content: \"\\f1e0\"; }\n\n* .fa-share-alt-square:before {\n content: \"\\f1e1\"; }\n\n* .fa-share-square:before {\n content: \"\\f14d\"; }\n\n* .fa-shekel-sign:before {\n content: \"\\f20b\"; }\n\n* .fa-shield-alt:before {\n content: \"\\f3ed\"; }\n\n* .fa-ship:before {\n content: \"\\f21a\"; }\n\n* .fa-shipping-fast:before {\n content: \"\\f48b\"; }\n\n* .fa-shirtsinbulk:before {\n content: \"\\f214\"; }\n\n* .fa-shoe-prints:before {\n content: \"\\f54b\"; }\n\n* .fa-shopping-bag:before {\n content: \"\\f290\"; }\n\n* .fa-shopping-basket:before {\n content: \"\\f291\"; }\n\n* .fa-shopping-cart:before {\n content: \"\\f07a\"; }\n\n* .fa-shopware:before {\n content: \"\\f5b5\"; }\n\n* .fa-shower:before {\n content: \"\\f2cc\"; }\n\n* .fa-shuttle-van:before {\n content: \"\\f5b6\"; }\n\n* .fa-sign:before {\n content: \"\\f4d9\"; }\n\n* .fa-sign-in-alt:before {\n content: \"\\f2f6\"; }\n\n* .fa-sign-language:before {\n content: \"\\f2a7\"; }\n\n* .fa-sign-out-alt:before {\n content: \"\\f2f5\"; }\n\n* .fa-signal:before {\n content: \"\\f012\"; }\n\n* .fa-signature:before {\n content: \"\\f5b7\"; }\n\n* .fa-sim-card:before {\n content: \"\\f7c4\"; }\n\n* .fa-simplybuilt:before {\n content: \"\\f215\"; }\n\n* .fa-sistrix:before {\n content: \"\\f3ee\"; }\n\n* .fa-sitemap:before {\n content: \"\\f0e8\"; }\n\n* .fa-sith:before {\n content: \"\\f512\"; }\n\n* .fa-skating:before {\n content: \"\\f7c5\"; }\n\n* .fa-sketch:before {\n content: \"\\f7c6\"; }\n\n* .fa-skiing:before {\n content: \"\\f7c9\"; }\n\n* .fa-skiing-nordic:before {\n content: \"\\f7ca\"; }\n\n* .fa-skull:before {\n content: \"\\f54c\"; }\n\n* .fa-skull-crossbones:before {\n content: \"\\f714\"; }\n\n* .fa-skyatlas:before {\n content: \"\\f216\"; }\n\n* .fa-skype:before {\n content: \"\\f17e\"; }\n\n* .fa-slack:before {\n content: \"\\f198\"; }\n\n* .fa-slack-hash:before {\n content: \"\\f3ef\"; }\n\n* .fa-slash:before {\n content: \"\\f715\"; }\n\n* .fa-sleigh:before {\n content: \"\\f7cc\"; }\n\n* .fa-sliders-h:before {\n content: \"\\f1de\"; }\n\n* .fa-slideshare:before {\n content: \"\\f1e7\"; }\n\n* .fa-smile:before {\n content: \"\\f118\"; }\n\n* .fa-smile-beam:before {\n content: \"\\f5b8\"; }\n\n* .fa-smile-wink:before {\n content: \"\\f4da\"; }\n\n* .fa-smog:before {\n content: \"\\f75f\"; }\n\n* .fa-smoking:before {\n content: \"\\f48d\"; }\n\n* .fa-smoking-ban:before {\n content: \"\\f54d\"; }\n\n* .fa-sms:before {\n content: \"\\f7cd\"; }\n\n* .fa-snapchat:before {\n content: \"\\f2ab\"; }\n\n* .fa-snapchat-ghost:before {\n content: \"\\f2ac\"; }\n\n* .fa-snapchat-square:before {\n content: \"\\f2ad\"; }\n\n* .fa-snowboarding:before {\n content: \"\\f7ce\"; }\n\n* .fa-snowflake:before {\n content: \"\\f2dc\"; }\n\n* .fa-snowman:before {\n content: \"\\f7d0\"; }\n\n* .fa-snowplow:before {\n content: \"\\f7d2\"; }\n\n* .fa-socks:before {\n content: \"\\f696\"; }\n\n* .fa-solar-panel:before {\n content: \"\\f5ba\"; }\n\n* .fa-sort:before {\n content: \"\\f0dc\"; }\n\n* .fa-sort-alpha-down:before {\n content: \"\\f15d\"; }\n\n* .fa-sort-alpha-up:before {\n content: \"\\f15e\"; }\n\n* .fa-sort-amount-down:before {\n content: \"\\f160\"; }\n\n* .fa-sort-amount-up:before {\n content: \"\\f161\"; }\n\n* .fa-sort-down:before {\n content: \"\\f0dd\"; }\n\n* .fa-sort-numeric-down:before {\n content: \"\\f162\"; }\n\n* .fa-sort-numeric-up:before {\n content: \"\\f163\"; }\n\n* .fa-sort-up:before {\n content: \"\\f0de\"; }\n\n* .fa-soundcloud:before {\n content: \"\\f1be\"; }\n\n* .fa-sourcetree:before {\n content: \"\\f7d3\"; }\n\n* .fa-spa:before {\n content: \"\\f5bb\"; }\n\n* .fa-space-shuttle:before {\n content: \"\\f197\"; }\n\n* .fa-speakap:before {\n content: \"\\f3f3\"; }\n\n* .fa-spider:before {\n content: \"\\f717\"; }\n\n* .fa-spinner:before {\n content: \"\\f110\"; }\n\n* .fa-splotch:before {\n content: \"\\f5bc\"; }\n\n* .fa-spotify:before {\n content: \"\\f1bc\"; }\n\n* .fa-spray-can:before {\n content: \"\\f5bd\"; }\n\n* .fa-square:before {\n content: \"\\f0c8\"; }\n\n* .fa-square-full:before {\n content: \"\\f45c\"; }\n\n* .fa-square-root-alt:before {\n content: \"\\f698\"; }\n\n* .fa-squarespace:before {\n content: \"\\f5be\"; }\n\n* .fa-stack-exchange:before {\n content: \"\\f18d\"; }\n\n* .fa-stack-overflow:before {\n content: \"\\f16c\"; }\n\n* .fa-stamp:before {\n content: \"\\f5bf\"; }\n\n* .fa-star:before {\n content: \"\\f005\"; }\n\n* .fa-star-and-crescent:before {\n content: \"\\f699\"; }\n\n* .fa-star-half:before {\n content: \"\\f089\"; }\n\n* .fa-star-half-alt:before {\n content: \"\\f5c0\"; }\n\n* .fa-star-of-david:before {\n content: \"\\f69a\"; }\n\n* .fa-star-of-life:before {\n content: \"\\f621\"; }\n\n* .fa-staylinked:before {\n content: \"\\f3f5\"; }\n\n* .fa-steam:before {\n content: \"\\f1b6\"; }\n\n* .fa-steam-square:before {\n content: \"\\f1b7\"; }\n\n* .fa-steam-symbol:before {\n content: \"\\f3f6\"; }\n\n* .fa-step-backward:before {\n content: \"\\f048\"; }\n\n* .fa-step-forward:before {\n content: \"\\f051\"; }\n\n* .fa-stethoscope:before {\n content: \"\\f0f1\"; }\n\n* .fa-sticker-mule:before {\n content: \"\\f3f7\"; }\n\n* .fa-sticky-note:before {\n content: \"\\f249\"; }\n\n* .fa-stop:before {\n content: \"\\f04d\"; }\n\n* .fa-stop-circle:before {\n content: \"\\f28d\"; }\n\n* .fa-stopwatch:before {\n content: \"\\f2f2\"; }\n\n* .fa-store:before {\n content: \"\\f54e\"; }\n\n* .fa-store-alt:before {\n content: \"\\f54f\"; }\n\n* .fa-strava:before {\n content: \"\\f428\"; }\n\n* .fa-stream:before {\n content: \"\\f550\"; }\n\n* .fa-street-view:before {\n content: \"\\f21d\"; }\n\n* .fa-strikethrough:before {\n content: \"\\f0cc\"; }\n\n* .fa-stripe:before {\n content: \"\\f429\"; }\n\n* .fa-stripe-s:before {\n content: \"\\f42a\"; }\n\n* .fa-stroopwafel:before {\n content: \"\\f551\"; }\n\n* .fa-studiovinari:before {\n content: \"\\f3f8\"; }\n\n* .fa-stumbleupon:before {\n content: \"\\f1a4\"; }\n\n* .fa-stumbleupon-circle:before {\n content: \"\\f1a3\"; }\n\n* .fa-subscript:before {\n content: \"\\f12c\"; }\n\n* .fa-subway:before {\n content: \"\\f239\"; }\n\n* .fa-suitcase:before {\n content: \"\\f0f2\"; }\n\n* .fa-suitcase-rolling:before {\n content: \"\\f5c1\"; }\n\n* .fa-sun:before {\n content: \"\\f185\"; }\n\n* .fa-superpowers:before {\n content: \"\\f2dd\"; }\n\n* .fa-superscript:before {\n content: \"\\f12b\"; }\n\n* .fa-supple:before {\n content: \"\\f3f9\"; }\n\n* .fa-surprise:before {\n content: \"\\f5c2\"; }\n\n* .fa-suse:before {\n content: \"\\f7d6\"; }\n\n* .fa-swatchbook:before {\n content: \"\\f5c3\"; }\n\n* .fa-swimmer:before {\n content: \"\\f5c4\"; }\n\n* .fa-swimming-pool:before {\n content: \"\\f5c5\"; }\n\n* .fa-synagogue:before {\n content: \"\\f69b\"; }\n\n* .fa-sync:before {\n content: \"\\f021\"; }\n\n* .fa-sync-alt:before {\n content: \"\\f2f1\"; }\n\n* .fa-syringe:before {\n content: \"\\f48e\"; }\n\n* .fa-table:before {\n content: \"\\f0ce\"; }\n\n* .fa-table-tennis:before {\n content: \"\\f45d\"; }\n\n* .fa-tablet:before {\n content: \"\\f10a\"; }\n\n* .fa-tablet-alt:before {\n content: \"\\f3fa\"; }\n\n* .fa-tablets:before {\n content: \"\\f490\"; }\n\n* .fa-tachometer-alt:before {\n content: \"\\f3fd\"; }\n\n* .fa-tag:before {\n content: \"\\f02b\"; }\n\n* .fa-tags:before {\n content: \"\\f02c\"; }\n\n* .fa-tape:before {\n content: \"\\f4db\"; }\n\n* .fa-tasks:before {\n content: \"\\f0ae\"; }\n\n* .fa-taxi:before {\n content: \"\\f1ba\"; }\n\n* .fa-teamspeak:before {\n content: \"\\f4f9\"; }\n\n* .fa-teeth:before {\n content: \"\\f62e\"; }\n\n* .fa-teeth-open:before {\n content: \"\\f62f\"; }\n\n* .fa-telegram:before {\n content: \"\\f2c6\"; }\n\n* .fa-telegram-plane:before {\n content: \"\\f3fe\"; }\n\n* .fa-temperature-high:before {\n content: \"\\f769\"; }\n\n* .fa-temperature-low:before {\n content: \"\\f76b\"; }\n\n* .fa-tencent-weibo:before {\n content: \"\\f1d5\"; }\n\n* .fa-tenge:before {\n content: \"\\f7d7\"; }\n\n* .fa-terminal:before {\n content: \"\\f120\"; }\n\n* .fa-text-height:before {\n content: \"\\f034\"; }\n\n* .fa-text-width:before {\n content: \"\\f035\"; }\n\n* .fa-th:before {\n content: \"\\f00a\"; }\n\n* .fa-th-large:before {\n content: \"\\f009\"; }\n\n* .fa-th-list:before {\n content: \"\\f00b\"; }\n\n* .fa-the-red-yeti:before {\n content: \"\\f69d\"; }\n\n* .fa-theater-masks:before {\n content: \"\\f630\"; }\n\n* .fa-themeco:before {\n content: \"\\f5c6\"; }\n\n* .fa-themeisle:before {\n content: \"\\f2b2\"; }\n\n* .fa-thermometer:before {\n content: \"\\f491\"; }\n\n* .fa-thermometer-empty:before {\n content: \"\\f2cb\"; }\n\n* .fa-thermometer-full:before {\n content: \"\\f2c7\"; }\n\n* .fa-thermometer-half:before {\n content: \"\\f2c9\"; }\n\n* .fa-thermometer-quarter:before {\n content: \"\\f2ca\"; }\n\n* .fa-thermometer-three-quarters:before {\n content: \"\\f2c8\"; }\n\n* .fa-think-peaks:before {\n content: \"\\f731\"; }\n\n* .fa-thumbs-down:before {\n content: \"\\f165\"; }\n\n* .fa-thumbs-up:before {\n content: \"\\f164\"; }\n\n* .fa-thumbtack:before {\n content: \"\\f08d\"; }\n\n* .fa-ticket-alt:before {\n content: \"\\f3ff\"; }\n\n* .fa-times:before {\n content: \"\\f00d\"; }\n\n* .fa-times-circle:before {\n content: \"\\f057\"; }\n\n* .fa-tint:before {\n content: \"\\f043\"; }\n\n* .fa-tint-slash:before {\n content: \"\\f5c7\"; }\n\n* .fa-tired:before {\n content: \"\\f5c8\"; }\n\n* .fa-toggle-off:before {\n content: \"\\f204\"; }\n\n* .fa-toggle-on:before {\n content: \"\\f205\"; }\n\n* .fa-toilet:before {\n content: \"\\f7d8\"; }\n\n* .fa-toilet-paper:before {\n content: \"\\f71e\"; }\n\n* .fa-toolbox:before {\n content: \"\\f552\"; }\n\n* .fa-tools:before {\n content: \"\\f7d9\"; }\n\n* .fa-tooth:before {\n content: \"\\f5c9\"; }\n\n* .fa-torah:before {\n content: \"\\f6a0\"; }\n\n* .fa-torii-gate:before {\n content: \"\\f6a1\"; }\n\n* .fa-tractor:before {\n content: \"\\f722\"; }\n\n* .fa-trade-federation:before {\n content: \"\\f513\"; }\n\n* .fa-trademark:before {\n content: \"\\f25c\"; }\n\n* .fa-traffic-light:before {\n content: \"\\f637\"; }\n\n* .fa-train:before {\n content: \"\\f238\"; }\n\n* .fa-tram:before {\n content: \"\\f7da\"; }\n\n* .fa-transgender:before {\n content: \"\\f224\"; }\n\n* .fa-transgender-alt:before {\n content: \"\\f225\"; }\n\n* .fa-trash:before {\n content: \"\\f1f8\"; }\n\n* .fa-trash-alt:before {\n content: \"\\f2ed\"; }\n\n* .fa-tree:before {\n content: \"\\f1bb\"; }\n\n* .fa-trello:before {\n content: \"\\f181\"; }\n\n* .fa-tripadvisor:before {\n content: \"\\f262\"; }\n\n* .fa-trophy:before {\n content: \"\\f091\"; }\n\n* .fa-truck:before {\n content: \"\\f0d1\"; }\n\n* .fa-truck-loading:before {\n content: \"\\f4de\"; }\n\n* .fa-truck-monster:before {\n content: \"\\f63b\"; }\n\n* .fa-truck-moving:before {\n content: \"\\f4df\"; }\n\n* .fa-truck-pickup:before {\n content: \"\\f63c\"; }\n\n* .fa-tshirt:before {\n content: \"\\f553\"; }\n\n* .fa-tty:before {\n content: \"\\f1e4\"; }\n\n* .fa-tumblr:before {\n content: \"\\f173\"; }\n\n* .fa-tumblr-square:before {\n content: \"\\f174\"; }\n\n* .fa-tv:before {\n content: \"\\f26c\"; }\n\n* .fa-twitch:before {\n content: \"\\f1e8\"; }\n\n* .fa-twitter:before {\n content: \"\\f099\"; }\n\n* .fa-twitter-square:before {\n content: \"\\f081\"; }\n\n* .fa-typo3:before {\n content: \"\\f42b\"; }\n\n* .fa-uber:before {\n content: \"\\f402\"; }\n\n* .fa-ubuntu:before {\n content: \"\\f7df\"; }\n\n* .fa-uikit:before {\n content: \"\\f403\"; }\n\n* .fa-umbrella:before {\n content: \"\\f0e9\"; }\n\n* .fa-umbrella-beach:before {\n content: \"\\f5ca\"; }\n\n* .fa-underline:before {\n content: \"\\f0cd\"; }\n\n* .fa-undo:before {\n content: \"\\f0e2\"; }\n\n* .fa-undo-alt:before {\n content: \"\\f2ea\"; }\n\n* .fa-uniregistry:before {\n content: \"\\f404\"; }\n\n* .fa-universal-access:before {\n content: \"\\f29a\"; }\n\n* .fa-university:before {\n content: \"\\f19c\"; }\n\n* .fa-unlink:before {\n content: \"\\f127\"; }\n\n* .fa-unlock:before {\n content: \"\\f09c\"; }\n\n* .fa-unlock-alt:before {\n content: \"\\f13e\"; }\n\n* .fa-untappd:before {\n content: \"\\f405\"; }\n\n* .fa-upload:before {\n content: \"\\f093\"; }\n\n* .fa-ups:before {\n content: \"\\f7e0\"; }\n\n* .fa-usb:before {\n content: \"\\f287\"; }\n\n* .fa-user:before {\n content: \"\\f007\"; }\n\n* .fa-user-alt:before {\n content: \"\\f406\"; }\n\n* .fa-user-alt-slash:before {\n content: \"\\f4fa\"; }\n\n* .fa-user-astronaut:before {\n content: \"\\f4fb\"; }\n\n* .fa-user-check:before {\n content: \"\\f4fc\"; }\n\n* .fa-user-circle:before {\n content: \"\\f2bd\"; }\n\n* .fa-user-clock:before {\n content: \"\\f4fd\"; }\n\n* .fa-user-cog:before {\n content: \"\\f4fe\"; }\n\n* .fa-user-edit:before {\n content: \"\\f4ff\"; }\n\n* .fa-user-friends:before {\n content: \"\\f500\"; }\n\n* .fa-user-graduate:before {\n content: \"\\f501\"; }\n\n* .fa-user-injured:before {\n content: \"\\f728\"; }\n\n* .fa-user-lock:before {\n content: \"\\f502\"; }\n\n* .fa-user-md:before {\n content: \"\\f0f0\"; }\n\n* .fa-user-minus:before {\n content: \"\\f503\"; }\n\n* .fa-user-ninja:before {\n content: \"\\f504\"; }\n\n* .fa-user-plus:before {\n content: \"\\f234\"; }\n\n* .fa-user-secret:before {\n content: \"\\f21b\"; }\n\n* .fa-user-shield:before {\n content: \"\\f505\"; }\n\n* .fa-user-slash:before {\n content: \"\\f506\"; }\n\n* .fa-user-tag:before {\n content: \"\\f507\"; }\n\n* .fa-user-tie:before {\n content: \"\\f508\"; }\n\n* .fa-user-times:before {\n content: \"\\f235\"; }\n\n* .fa-users:before {\n content: \"\\f0c0\"; }\n\n* .fa-users-cog:before {\n content: \"\\f509\"; }\n\n* .fa-usps:before {\n content: \"\\f7e1\"; }\n\n* .fa-ussunnah:before {\n content: \"\\f407\"; }\n\n* .fa-utensil-spoon:before {\n content: \"\\f2e5\"; }\n\n* .fa-utensils:before {\n content: \"\\f2e7\"; }\n\n* .fa-vaadin:before {\n content: \"\\f408\"; }\n\n* .fa-vector-square:before {\n content: \"\\f5cb\"; }\n\n* .fa-venus:before {\n content: \"\\f221\"; }\n\n* .fa-venus-double:before {\n content: \"\\f226\"; }\n\n* .fa-venus-mars:before {\n content: \"\\f228\"; }\n\n* .fa-viacoin:before {\n content: \"\\f237\"; }\n\n* .fa-viadeo:before {\n content: \"\\f2a9\"; }\n\n* .fa-viadeo-square:before {\n content: \"\\f2aa\"; }\n\n* .fa-vial:before {\n content: \"\\f492\"; }\n\n* .fa-vials:before {\n content: \"\\f493\"; }\n\n* .fa-viber:before {\n content: \"\\f409\"; }\n\n* .fa-video:before {\n content: \"\\f03d\"; }\n\n* .fa-video-slash:before {\n content: \"\\f4e2\"; }\n\n* .fa-vihara:before {\n content: \"\\f6a7\"; }\n\n* .fa-vimeo:before {\n content: \"\\f40a\"; }\n\n* .fa-vimeo-square:before {\n content: \"\\f194\"; }\n\n* .fa-vimeo-v:before {\n content: \"\\f27d\"; }\n\n* .fa-vine:before {\n content: \"\\f1ca\"; }\n\n* .fa-vk:before {\n content: \"\\f189\"; }\n\n* .fa-vnv:before {\n content: \"\\f40b\"; }\n\n* .fa-volleyball-ball:before {\n content: \"\\f45f\"; }\n\n* .fa-volume-down:before {\n content: \"\\f027\"; }\n\n* .fa-volume-mute:before {\n content: \"\\f6a9\"; }\n\n* .fa-volume-off:before {\n content: \"\\f026\"; }\n\n* .fa-volume-up:before {\n content: \"\\f028\"; }\n\n* .fa-vote-yea:before {\n content: \"\\f772\"; }\n\n* .fa-vr-cardboard:before {\n content: \"\\f729\"; }\n\n* .fa-vuejs:before {\n content: \"\\f41f\"; }\n\n* .fa-walking:before {\n content: \"\\f554\"; }\n\n* .fa-wallet:before {\n content: \"\\f555\"; }\n\n* .fa-warehouse:before {\n content: \"\\f494\"; }\n\n* .fa-water:before {\n content: \"\\f773\"; }\n\n* .fa-weebly:before {\n content: \"\\f5cc\"; }\n\n* .fa-weibo:before {\n content: \"\\f18a\"; }\n\n* .fa-weight:before {\n content: \"\\f496\"; }\n\n* .fa-weight-hanging:before {\n content: \"\\f5cd\"; }\n\n* .fa-weixin:before {\n content: \"\\f1d7\"; }\n\n* .fa-whatsapp:before {\n content: \"\\f232\"; }\n\n* .fa-whatsapp-square:before {\n content: \"\\f40c\"; }\n\n* .fa-wheelchair:before {\n content: \"\\f193\"; }\n\n* .fa-whmcs:before {\n content: \"\\f40d\"; }\n\n* .fa-wifi:before {\n content: \"\\f1eb\"; }\n\n* .fa-wikipedia-w:before {\n content: \"\\f266\"; }\n\n* .fa-wind:before {\n content: \"\\f72e\"; }\n\n* .fa-window-close:before {\n content: \"\\f410\"; }\n\n* .fa-window-maximize:before {\n content: \"\\f2d0\"; }\n\n* .fa-window-minimize:before {\n content: \"\\f2d1\"; }\n\n* .fa-window-restore:before {\n content: \"\\f2d2\"; }\n\n* .fa-windows:before {\n content: \"\\f17a\"; }\n\n* .fa-wine-bottle:before {\n content: \"\\f72f\"; }\n\n* .fa-wine-glass:before {\n content: \"\\f4e3\"; }\n\n* .fa-wine-glass-alt:before {\n content: \"\\f5ce\"; }\n\n* .fa-wix:before {\n content: \"\\f5cf\"; }\n\n* .fa-wizards-of-the-coast:before {\n content: \"\\f730\"; }\n\n* .fa-wolf-pack-battalion:before {\n content: \"\\f514\"; }\n\n* .fa-won-sign:before {\n content: \"\\f159\"; }\n\n* .fa-wordpress:before {\n content: \"\\f19a\"; }\n\n* .fa-wordpress-simple:before {\n content: \"\\f411\"; }\n\n* .fa-wpbeginner:before {\n content: \"\\f297\"; }\n\n* .fa-wpexplorer:before {\n content: \"\\f2de\"; }\n\n* .fa-wpforms:before {\n content: \"\\f298\"; }\n\n* .fa-wpressr:before {\n content: \"\\f3e4\"; }\n\n* .fa-wrench:before {\n content: \"\\f0ad\"; }\n\n* .fa-x-ray:before {\n content: \"\\f497\"; }\n\n* .fa-xbox:before {\n content: \"\\f412\"; }\n\n* .fa-xing:before {\n content: \"\\f168\"; }\n\n* .fa-xing-square:before {\n content: \"\\f169\"; }\n\n* .fa-y-combinator:before {\n content: \"\\f23b\"; }\n\n* .fa-yahoo:before {\n content: \"\\f19e\"; }\n\n* .fa-yandex:before {\n content: \"\\f413\"; }\n\n* .fa-yandex-international:before {\n content: \"\\f414\"; }\n\n* .fa-yarn:before {\n content: \"\\f7e3\"; }\n\n* .fa-yelp:before {\n content: \"\\f1e9\"; }\n\n* .fa-yen-sign:before {\n content: \"\\f157\"; }\n\n* .fa-yin-yang:before {\n content: \"\\f6ad\"; }\n\n* .fa-yoast:before {\n content: \"\\f2b1\"; }\n\n* .fa-youtube:before {\n content: \"\\f167\"; }\n\n* .fa-youtube-square:before {\n content: \"\\f431\"; }\n\n* .fa-zhihu:before {\n content: \"\\f63f\"; }\n\n* .sr-only {\n border: 0;\n clip: rect(0, 0, 0, 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px; }\n\n* .sr-only-focusable:active, * .sr-only-focusable:focus {\n clip: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n position: static;\n width: auto; }\n\n@font-face {\n font-family: \"Font Awesome 5 Free\";\n font-style: normal;\n font-weight: 900;\n src: url(\"./assets/fonts/webfonts/fa-solid-900.eot\");\n src: url(\"./assets/fonts/webfonts/fa-solid-900.eot?#iefix\") format(\"embedded-opentype\"), url(\"./assets/fonts/webfonts/fa-solid-900.woff2\") format(\"woff2\"), url(\"./assets/fonts/webfonts/fa-solid-900.woff\") format(\"woff\"), url(\"./assets/fonts/webfonts/fa-solid-900.ttf\") format(\"truetype\"), url(\"./assets/fonts/webfonts/fa-solid-900.svg#fontawesome\") format(\"svg\"); }\n\n.fa,\n.fas {\n font-family: \"Font Awesome 5 Free\";\n font-weight: 900; }\n\n@font-face {\n font-family: \"pficon\";\n src: url(\"./assets/pficon/pficon.eot\");\n src: url(\"./assets/pficon/pficon.eot?#iefix\") format(\"eot\"), url(\"./assets/pficon/pficon.woff2\") format(\"woff2\"), url(\"./assets/pficon/pficon.woff\") format(\"woff\"), url(\"./assets/pficon/pficon.ttf\") format(\"truetype\"), url(\"./assets/pficon/pficon.svg#pficon\") format(\"svg\"); }\n\n.pf-icon-aa-circle-o:before, .pf-icon-add-circle-o:before, .pf-icon-ansible-tower:before, .pf-icon-applications:before, .pf-icon-arrow:before, .pf-icon-asleep:before, .pf-icon-attention-bell:before, .pf-icon-automation:before, .pf-icon-bell:before, .pf-icon-blueprint:before, .pf-icon-build:before, .pf-icon-builder-image:before, .pf-icon-bundle:before, .pf-icon-catalog:before, .pf-icon-chat:before, .pf-icon-close:before, .pf-icon-cloud-security:before, .pf-icon-cloud-tenant:before, .pf-icon-cluster:before, .pf-icon-connected:before, .pf-icon-container-node:before, .pf-icon-cpu:before, .pf-icon-degraded:before, .pf-icon-disconnected:before, .pf-icon-domain:before, .pf-icon-edit:before, .pf-icon-enhancement:before, .pf-icon-enterprise:before, .pf-icon-equalizer:before, .pf-icon-error-circle-o:before, .pf-icon-export:before, .pf-icon-filter:before, .pf-icon-flavor:before, .pf-icon-folder-close:before, .pf-icon-folder-open:before, .pf-icon-globe-route:before, .pf-icon-help:before, .pf-icon-history:before, .pf-icon-home:before, .pf-icon-import:before, .pf-icon-in-progress:before, .pf-icon-info:before, .pf-icon-infrastructure:before, .pf-icon-integration:before, .pf-icon-key:before, .pf-icon-locked:before, .pf-icon-maintenance:before, .pf-icon-memory:before, .pf-icon-messages:before, .pf-icon-middleware:before, .pf-icon-migration:before, .pf-icon-module:before, .pf-icon-monitoring:before, .pf-icon-namespaces:before, .pf-icon-network:before, .pf-icon-new-process:before, .pf-icon-not-started:before, .pf-icon-off:before, .pf-icon-ok:before, .pf-icon-on-running:before, .pf-icon-on:before, .pf-icon-openshift:before, .pf-icon-openstack:before, .pf-icon-optimize:before, .pf-icon-orders:before, .pf-icon-os-image:before, .pf-icon-package:before, .pf-icon-paused:before, .pf-icon-pending:before, .pf-icon-pficon-dragdrop:before, .pf-icon-pficon-history:before, .pf-icon-pficon-network-range:before, .pf-icon-pficon-satellite:before, .pf-icon-pficon-sort-common-asc:before, .pf-icon-pficon-sort-common-desc:before, .pf-icon-pficon-template:before, .pf-icon-pficon-vcenter:before, .pf-icon-plugged:before, .pf-icon-port:before, .pf-icon-print:before, .pf-icon-private:before, .pf-icon-process-automation:before, .pf-icon-project:before, .pf-icon-rebalance:before, .pf-icon-rebooting:before, .pf-icon-regions:before, .pf-icon-registry:before, .pf-icon-remove2:before, .pf-icon-replicator:before, .pf-icon-repository:before, .pf-icon-resource-pool:before, .pf-icon-resources-almost-empty:before, .pf-icon-resources-almost-full:before, .pf-icon-resources-empty:before, .pf-icon-resources-full:before, .pf-icon-running:before, .pf-icon-save:before, .pf-icon-screen:before, .pf-icon-security:before, .pf-icon-server-group:before, .pf-icon-server:before, .pf-icon-service-catalog:before, .pf-icon-service:before, .pf-icon-services:before, .pf-icon-spinner:before, .pf-icon-spinner2:before, .pf-icon-storage-domain:before, .pf-icon-tenant:before, .pf-icon-thumb-tack:before, .pf-icon-topology:before, .pf-icon-trend-down:before, .pf-icon-trend-up:before, .pf-icon-unknown:before, .pf-icon-unlocked:before, .pf-icon-unplugged:before, .pf-icon-user:before, .pf-icon-users:before, .pf-icon-virtual-machine:before, .pf-icon-volume:before, .pf-icon-warning-triangle:before, .pf-icon-zone:before {\n font-family: \"pficon\";\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n font-style: normal;\n font-variant: normal;\n font-weight: normal;\n text-decoration: none;\n text-transform: none; }\n\n.pf-icon-aa-circle-o:before {\n content: \"\"; }\n\n.pf-icon-add-circle-o:before {\n content: \"\"; }\n\n.pf-icon-ansible-tower:before {\n content: \"\"; }\n\n.pf-icon-applications:before {\n content: \"\"; }\n\n.pf-icon-arrow:before {\n content: \"\"; }\n\n.pf-icon-asleep:before {\n content: \"\"; }\n\n.pf-icon-attention-bell:before {\n content: \"\"; }\n\n.pf-icon-automation:before {\n content: \"\"; }\n\n.pf-icon-bell:before {\n content: \"\"; }\n\n.pf-icon-blueprint:before {\n content: \"\"; }\n\n.pf-icon-build:before {\n content: \"\"; }\n\n.pf-icon-builder-image:before {\n content: \"\"; }\n\n.pf-icon-bundle:before {\n content: \"\"; }\n\n.pf-icon-catalog:before {\n content: \"\"; }\n\n.pf-icon-chat:before {\n content: \"\"; }\n\n.pf-icon-close:before {\n content: \"\"; }\n\n.pf-icon-cloud-security:before {\n content: \"\"; }\n\n.pf-icon-cloud-tenant:before {\n content: \"\"; }\n\n.pf-icon-cluster:before {\n content: \"\"; }\n\n.pf-icon-connected:before {\n content: \"\"; }\n\n.pf-icon-container-node:before {\n content: \"\"; }\n\n.pf-icon-cpu:before {\n content: \"\"; }\n\n.pf-icon-degraded:before {\n content: \"\"; }\n\n.pf-icon-disconnected:before {\n content: \"\"; }\n\n.pf-icon-domain:before {\n content: \"\"; }\n\n.pf-icon-edit:before {\n content: \"\"; }\n\n.pf-icon-enhancement:before {\n content: \"\"; }\n\n.pf-icon-enterprise:before {\n content: \"\"; }\n\n.pf-icon-equalizer:before {\n content: \"\"; }\n\n.pf-icon-error-circle-o:before {\n content: \"\"; }\n\n.pf-icon-export:before {\n content: \"\"; }\n\n.pf-icon-filter:before {\n content: \"\"; }\n\n.pf-icon-flavor:before {\n content: \"\"; }\n\n.pf-icon-folder-close:before {\n content: \"\"; }\n\n.pf-icon-folder-open:before {\n content: \"\"; }\n\n.pf-icon-globe-route:before {\n content: \"\"; }\n\n.pf-icon-help:before {\n content: \"\"; }\n\n.pf-icon-history:before {\n content: \"\"; }\n\n.pf-icon-home:before {\n content: \"\"; }\n\n.pf-icon-import:before {\n content: \"\"; }\n\n.pf-icon-in-progress:before {\n content: \"\"; }\n\n.pf-icon-info:before {\n content: \"\"; }\n\n.pf-icon-infrastructure:before {\n content: \"\"; }\n\n.pf-icon-integration:before {\n content: \"\"; }\n\n.pf-icon-key:before {\n content: \"\"; }\n\n.pf-icon-locked:before {\n content: \"\"; }\n\n.pf-icon-maintenance:before {\n content: \"\"; }\n\n.pf-icon-memory:before {\n content: \"\"; }\n\n.pf-icon-messages:before {\n content: \"\"; }\n\n.pf-icon-middleware:before {\n content: \"\"; }\n\n.pf-icon-migration:before {\n content: \"\"; }\n\n.pf-icon-module:before {\n content: \"\"; }\n\n.pf-icon-monitoring:before {\n content: \"\"; }\n\n.pf-icon-namespaces:before {\n content: \"\"; }\n\n.pf-icon-network:before {\n content: \"\"; }\n\n.pf-icon-new-process:before {\n content: \"\"; }\n\n.pf-icon-not-started:before {\n content: \"\"; }\n\n.pf-icon-off:before {\n content: \"\"; }\n\n.pf-icon-ok:before {\n content: \"\"; }\n\n.pf-icon-on-running:before {\n content: \"\"; }\n\n.pf-icon-on:before {\n content: \"\"; }\n\n.pf-icon-openshift:before {\n content: \"\"; }\n\n.pf-icon-openstack:before {\n content: \"\"; }\n\n.pf-icon-optimize:before {\n content: \"\"; }\n\n.pf-icon-orders:before {\n content: \"\"; }\n\n.pf-icon-os-image:before {\n content: \"\"; }\n\n.pf-icon-package:before {\n content: \"\"; }\n\n.pf-icon-paused:before {\n content: \"\"; }\n\n.pf-icon-pending:before {\n content: \"\"; }\n\n.pf-icon-pficon-dragdrop:before {\n content: \"\"; }\n\n.pf-icon-pficon-history:before {\n content: \"\"; }\n\n.pf-icon-pficon-network-range:before {\n content: \"\"; }\n\n.pf-icon-pficon-satellite:before {\n content: \"\"; }\n\n.pf-icon-pficon-sort-common-asc:before {\n content: \"\"; }\n\n.pf-icon-pficon-sort-common-desc:before {\n content: \"\"; }\n\n.pf-icon-pficon-template:before {\n content: \"\"; }\n\n.pf-icon-pficon-vcenter:before {\n content: \"\"; }\n\n.pf-icon-plugged:before {\n content: \"\"; }\n\n.pf-icon-port:before {\n content: \"\"; }\n\n.pf-icon-print:before {\n content: \"\"; }\n\n.pf-icon-private:before {\n content: \"\"; }\n\n.pf-icon-process-automation:before {\n content: \"\"; }\n\n.pf-icon-project:before {\n content: \"\"; }\n\n.pf-icon-rebalance:before {\n content: \"\"; }\n\n.pf-icon-rebooting:before {\n content: \"\"; }\n\n.pf-icon-regions:before {\n content: \"\"; }\n\n.pf-icon-registry:before {\n content: \"\"; }\n\n.pf-icon-remove2:before {\n content: \"\"; }\n\n.pf-icon-replicator:before {\n content: \"\"; }\n\n.pf-icon-repository:before {\n content: \"\"; }\n\n.pf-icon-resource-pool:before {\n content: \"\"; }\n\n.pf-icon-resources-almost-empty:before {\n content: \"\"; }\n\n.pf-icon-resources-almost-full:before {\n content: \"\"; }\n\n.pf-icon-resources-empty:before {\n content: \"\"; }\n\n.pf-icon-resources-full:before {\n content: \"\"; }\n\n.pf-icon-running:before {\n content: \"\"; }\n\n.pf-icon-save:before {\n content: \"\"; }\n\n.pf-icon-screen:before {\n content: \"\"; }\n\n.pf-icon-security:before {\n content: \"\"; }\n\n.pf-icon-server-group:before {\n content: \"\"; }\n\n.pf-icon-server:before {\n content: \"\"; }\n\n.pf-icon-service-catalog:before {\n content: \"\"; }\n\n.pf-icon-service:before {\n content: \"\"; }\n\n.pf-icon-services:before {\n content: \"\"; }\n\n.pf-icon-spinner:before {\n content: \"\"; }\n\n.pf-icon-spinner2:before {\n content: \"\"; }\n\n.pf-icon-storage-domain:before {\n content: \"\"; }\n\n.pf-icon-tenant:before {\n content: \"\"; }\n\n.pf-icon-thumb-tack:before {\n content: \"\"; }\n\n.pf-icon-topology:before {\n content: \"\"; }\n\n.pf-icon-trend-down:before {\n content: \"\"; }\n\n.pf-icon-trend-up:before {\n content: \"\"; }\n\n.pf-icon-unknown:before {\n content: \"\"; }\n\n.pf-icon-unlocked:before {\n content: \"\"; }\n\n.pf-icon-unplugged:before {\n content: \"\"; }\n\n.pf-icon-user:before {\n content: \"\"; }\n\n.pf-icon-users:before {\n content: \"\"; }\n\n.pf-icon-virtual-machine:before {\n content: \"\"; }\n\n.pf-icon-volume:before {\n content: \"\"; }\n\n.pf-icon-warning-triangle:before {\n content: \"\"; }\n\n.pf-icon-zone:before {\n content: \"\"; }\n\n.pf-c-about-modal-box {\n --pf-c-about-modal-box--BackgroundColor: var(--pf-global--palette--black-1000);\n --pf-c-about-modal-box--BoxShadow: 0 0 100px 0 rgba(255, 255, 255, .05);\n --pf-c-about-modal-box--ZIndex: var(--pf-global--ZIndex--xl);\n --pf-c-about-modal-box--Height: 100%;\n --pf-c-about-modal-box--lg--Height: 47.625rem;\n --pf-c-about-modal-box--lg--MaxHeight: calc(100% - var(--pf-global--spacer--xl));\n --pf-c-about-modal-box--Width: 100vw;\n --pf-c-about-modal-box--lg--Width: calc(100% - (var(--pf-global--spacer--3xl) * 2));\n --pf-c-about-modal-box--lg--MaxWidth: 77rem;\n --pf-c-about-modal-box--PaddingTop: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box--sm--PaddingTop: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box--sm--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box--sm--PaddingBottom: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box--sm--PaddingLeft: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box--sm--grid-template-columns: 5fr 1fr;\n --pf-c-about-modal-box--lg--grid-template-columns: 1fr .6fr;\n --pf-c-about-modal-box__brand--PaddingTop: var(--pf-global--spacer--2xl);\n --pf-c-about-modal-box__brand--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__brand--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__brand--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__brand--sm--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__brand--sm--PaddingLeft: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__brand--sm--PaddingBottom: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__close--ZIndex: var(--pf-global--ZIndex--2xl);\n --pf-c-about-modal-box__close--PaddingTop: var(--pf-global--spacer--2xl);\n --pf-c-about-modal-box__close--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__close--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__close--sm--PaddingBottom: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__close--sm--PaddingRight: 0;\n --pf-c-about-modal-box__close--lg--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__close--c-button--Color: var(--pf-global--Color--100);\n --pf-c-about-modal-box__close--c-button--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-about-modal-box__close--c-button--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-about-modal-box__close--c-button--Width: calc(var(--pf-c-about-modal-box__close--c-button--FontSize) * 2);\n --pf-c-about-modal-box__close--c-button--Height: calc(var(--pf-c-about-modal-box__close--c-button--FontSize) * 2);\n --pf-c-about-modal-box__close--c-button--BackgroundColor: var(--pf-global--palette--black-1000);\n --pf-c-about-modal-box__close--c-button--hover--BackgroundColor: rgba(3, 3, 3, 0.4);\n --pf-c-about-modal-box__hero--sm--BackgroundImage: url(\"./assets/images/pfbg_992@2x.jpg\");\n --pf-c-about-modal-box__hero--sm--BackgroundPosition: top left;\n --pf-c-about-modal-box__hero--sm--BackgroundSize: cover;\n --pf-c-about-modal-box__brand-image--Height: 2.5rem;\n --pf-c-about-modal-box__header--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__header--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-about-modal-box__header--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__header--sm--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__header--sm--PaddingLeft: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__strapline--PaddingTop: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__strapline--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-about-modal-box__strapline--sm--PaddingTop: var(--pf-global--spacer--2xl);\n --pf-c-about-modal-box__content--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__content--MarginRight: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__content--MarginBottom: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__content--MarginLeft: var(--pf-global--spacer--xl);\n --pf-c-about-modal-box__content--sm--MarginTop: var(--pf-global--spacer--2xl);\n --pf-c-about-modal-box__content--sm--MarginRight: var(--pf-global--spacer--3xl);\n --pf-c-about-modal-box__content--sm--MarginBottom: var(--pf-global--spacer--2xl);\n --pf-c-about-modal-box__content--sm--MarginLeft: var(--pf-global--spacer--3xl);\n color: var(--pf-global--Color--100);\n position: relative;\n z-index: var(--pf-c-about-modal-box--ZIndex);\n display: grid;\n grid-template-rows: max-content max-content auto;\n grid-template-areas: \"brand close\" \"header header\" \"content content\";\n width: var(--pf-c-about-modal-box--Width);\n height: var(--pf-c-about-modal-box--Height);\n overflow-x: hidden;\n overflow-y: auto;\n background-color: var(--pf-c-about-modal-box--BackgroundColor);\n box-shadow: var(--pf-c-about-modal-box--BoxShadow); }\n @media screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box--PaddingTop: var(--pf-c-about-modal-box--sm--PaddingTop);\n --pf-c-about-modal-box--PaddingRight: var(--pf-c-about-modal-box--sm--PaddingRight);\n --pf-c-about-modal-box--PaddingBottom: var(--pf-c-about-modal-box--sm--PaddingBottom);\n --pf-c-about-modal-box--PaddingLeft: var(--pf-c-about-modal-box--sm--PaddingLeft); } }\n @media screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box__brand--PaddingRight: var(--pf-c-about-modal-box__brand--sm--PaddingRight);\n --pf-c-about-modal-box__brand--PaddingLeft: var(--pf-c-about-modal-box__brand--sm--PaddingLeft);\n --pf-c-about-modal-box__brand--PaddingBottom: var(--pf-c-about-modal-box__brand--sm--PaddingBottom); } }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box__close--PaddingRight: var(--pf-c-about-modal-box__close--sm--PaddingRight);\n --pf-c-about-modal-box__close--PaddingBottom: var(--pf-c-about-modal-box__close--sm--PaddingBottom); } }\n @media only screen and (min-width: 992px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box__close--PaddingRight: var(--pf-c-about-modal-box__close--lg--PaddingRight); } }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box__header--PaddingRight: var(--pf-c-about-modal-box__header--sm--PaddingRight);\n --pf-c-about-modal-box__header--PaddingLeft: var(--pf-c-about-modal-box__header--sm--PaddingLeft); } }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box__strapline--PaddingTop: var(--pf-c-about-modal-box__strapline--sm--PaddingTop); } }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box__content--MarginTop: var(--pf-c-about-modal-box__content--sm--MarginTop);\n --pf-c-about-modal-box__content--MarginRight: var(--pf-c-about-modal-box__content--sm--MarginRight);\n --pf-c-about-modal-box__content--MarginBottom: var(--pf-c-about-modal-box__content--sm--MarginBottom);\n --pf-c-about-modal-box__content--MarginLeft: var(--pf-c-about-modal-box__content--sm--MarginLeft); } }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box {\n grid-template-columns: var(--pf-c-about-modal-box--sm--grid-template-columns);\n grid-template-areas: \"brand hero\" \"header hero\" \"content hero\"; } }\n @media only screen and (min-width: 992px) {\n .pf-c-about-modal-box {\n --pf-c-about-modal-box--Height: var(--pf-c-about-modal-box--lg--Height);\n --pf-c-about-modal-box--Width: var(--pf-c-about-modal-box--lg--Width);\n grid-template-columns: var(--pf-c-about-modal-box--lg--grid-template-columns);\n grid-template-rows: max-content max-content auto;\n max-width: var(--pf-c-about-modal-box--lg--MaxWidth);\n max-height: var(--pf-c-about-modal-box--lg--MaxHeight); } }\n\n.pf-c-about-modal-box__brand {\n grid-area: brand;\n display: flex;\n padding: var(--pf-c-about-modal-box__brand--PaddingTop) var(--pf-c-about-modal-box__brand--PaddingRight) var(--pf-c-about-modal-box__brand--PaddingBottom) var(--pf-c-about-modal-box__brand--PaddingLeft); }\n\n.pf-c-about-modal-box__brand-image {\n height: var(--pf-c-about-modal-box__brand-image--Height); }\n\n.pf-c-about-modal-box__header {\n grid-area: header;\n display: flex;\n flex-direction: column;\n padding-right: var(--pf-c-about-modal-box__header--PaddingRight);\n padding-bottom: var(--pf-c-about-modal-box__header--PaddingBottom);\n padding-left: var(--pf-c-about-modal-box__header--PaddingLeft); }\n\n.pf-c-about-modal-box__strapline {\n padding-top: var(--pf-c-about-modal-box__strapline--PaddingTop);\n margin-top: auto;\n font-size: var(--pf-c-about-modal-box__strapline--FontSize); }\n\n.pf-c-about-modal-box__content {\n display: flex;\n flex-direction: column;\n grid-area: content;\n margin: var(--pf-c-about-modal-box__content--MarginTop) var(--pf-c-about-modal-box__content--MarginRight) var(--pf-c-about-modal-box__content--MarginBottom) var(--pf-c-about-modal-box__content--MarginLeft);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n -webkit-overflow-scrolling: touch;\n word-break: break-word; }\n @media screen and (min-width: 576px) {\n .pf-c-about-modal-box__content {\n overflow: visible;\n overscroll-behavior: auto; } }\n\n.pf-c-about-modal-box__close {\n grid-area: close;\n position: sticky;\n top: 0;\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n padding-top: var(--pf-c-about-modal-box__close--PaddingTop);\n padding-right: var(--pf-c-about-modal-box__close--PaddingRight);\n padding-bottom: var(--pf-c-about-modal-box__close--PaddingBottom); }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box__close {\n grid-area: 1 / 2;\n justify-content: center; } }\n @media only screen and (min-width: 992px) {\n .pf-c-about-modal-box__close {\n justify-content: flex-end; } }\n .pf-c-about-modal-box__close .pf-c-button.pf-m-plain {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--pf-c-about-modal-box__close--c-button--Width);\n height: var(--pf-c-about-modal-box__close--c-button--Height);\n font-size: var(--pf-c-about-modal-box__close--c-button--FontSize);\n color: var(--pf-c-about-modal-box__close--c-button--Color);\n background-color: var(--pf-c-about-modal-box__close--c-button--BackgroundColor);\n border-radius: var(--pf-c-about-modal-box__close--c-button--BorderRadius); }\n .pf-c-about-modal-box__close .pf-c-button.pf-m-plain:hover {\n --pf-c-about-modal-box__close--c-button--BackgroundColor: var(--pf-c-about-modal-box__close--c-button--hover--BackgroundColor); }\n\n.pf-c-about-modal-box__hero {\n display: none;\n visibility: hidden; }\n @media only screen and (min-width: 576px) {\n .pf-c-about-modal-box__hero {\n display: block;\n visibility: visible;\n background-image: var(--pf-c-about-modal-box__hero--sm--BackgroundImage);\n background-repeat: no-repeat;\n background-attachment: fixed;\n background-position: var(--pf-c-about-modal-box__hero--sm--BackgroundPosition);\n background-size: var(--pf-c-about-modal-box__hero--sm--BackgroundSize);\n grid-area: hero; } }\n\n.pf-c-accordion {\n --pf-c-accordion--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-accordion__toggle--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-accordion__toggle--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-accordion__toggle--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-accordion__toggle--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-accordion__toggle--before--BackgroundColor: transparent;\n --pf-c-accordion__toggle--hover--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-accordion__toggle--focus--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-accordion__toggle--active--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-accordion__toggle--before--Width: var(--pf-global--BorderWidth--lg);\n --pf-c-accordion__toggle--m-expanded--before--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-accordion__toggle-text--MaxWidth: calc(100% - var(--pf-global--spacer--lg));\n --pf-c-accordion__toggle--hover__toggle-text--Color: var(--pf-global--link--Color);\n --pf-c-accordion__toggle--active__toggle-text--Color: var(--pf-global--link--Color);\n --pf-c-accordion__toggle--active__toggle-text--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-accordion__toggle--focus__toggle-text--Color: var(--pf-global--link--Color);\n --pf-c-accordion__toggle--focus__toggle-text--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-accordion__toggle--m-expanded__toggle-text--Color: var(--pf-global--link--Color);\n --pf-c-accordion__toggle--m-expanded__toggle-text--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-accordion__toggle-icon--Transition: .2s ease-in 0s;\n --pf-c-accordion__toggle--m-expanded__toggle-icon--Rotate: 90deg;\n --pf-c-accordion__expanded-content-body--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-accordion__expanded-content-body--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-accordion__expanded-content-body--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-accordion__expanded-content-body--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-accordion__expanded-content--Color: var(--pf-global--Color--200);\n --pf-c-accordion__expanded-content--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-accordion__expanded-content-body--before--BackgroundColor: transparent;\n --pf-c-accordion__expanded-content-body--before--Width: var(--pf-global--BorderWidth--lg);\n --pf-c-accordion__expanded-content--m-expanded__expanded-content-body--before--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-accordion__expanded-content--m-fixed--MaxHeight: 9.375rem;\n color: var(--pf-global--Color--100);\n background-color: var(--pf-c-accordion--BackgroundColor); }\n\n.pf-c-accordion__toggle {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: var(--pf-c-accordion__toggle--PaddingTop) var(--pf-c-accordion__toggle--PaddingRight) var(--pf-c-accordion__toggle--PaddingBottom) var(--pf-c-accordion__toggle--PaddingLeft);\n border: 0; }\n .pf-c-accordion__toggle::before {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: var(--pf-c-accordion__toggle--before--Width);\n content: \"\";\n background-color: var(--pf-c-accordion__toggle--before--BackgroundColor); }\n .pf-c-accordion__toggle.pf-m-expanded {\n --pf-c-accordion__toggle--before--BackgroundColor: var(--pf-c-accordion__toggle--m-expanded--before--BackgroundColor); }\n .pf-c-accordion__toggle.pf-m-expanded .pf-c-accordion__toggle-text {\n font-weight: var(--pf-c-accordion__toggle--m-expanded__toggle-text--FontWeight);\n color: var(--pf-c-accordion__toggle--m-expanded__toggle-text--Color); }\n .pf-c-accordion__toggle.pf-m-expanded .pf-c-accordion__toggle-icon {\n transform: rotate(var(--pf-c-accordion__toggle--m-expanded__toggle-icon--Rotate)); }\n .pf-c-accordion__toggle:hover {\n background-color: var(--pf-c-accordion__toggle--hover--BackgroundColor); }\n .pf-c-accordion__toggle:hover .pf-c-accordion__toggle-text {\n color: var(--pf-c-accordion__toggle--hover__toggle-text--Color); }\n .pf-c-accordion__toggle:focus {\n background-color: var(--pf-c-accordion__toggle--focus--BackgroundColor); }\n .pf-c-accordion__toggle:focus .pf-c-accordion__toggle-text {\n font-weight: var(--pf-c-accordion__toggle--focus__toggle-text--FontWeight);\n color: var(--pf-c-accordion__toggle--focus__toggle-text--Color); }\n .pf-c-accordion__toggle:active {\n background-color: var(--pf-c-accordion__toggle--active--BackgroundColor); }\n .pf-c-accordion__toggle:active .pf-c-accordion__toggle-text {\n font-weight: var(--pf-c-accordion__toggle--active__toggle-text--FontWeight);\n color: var(--pf-c-accordion__toggle--active__toggle-text--Color); }\n\n.pf-c-accordion__toggle-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: var(--pf-c-accordion__toggle-text--MaxWidth); }\n\n.pf-c-accordion__toggle-icon {\n transition: var(--pf-c-accordion__toggle-icon--Transition); }\n\n.pf-c-accordion__expanded-content {\n font-size: var(--pf-c-accordion__expanded-content--FontSize);\n color: var(--pf-c-accordion__expanded-content--Color); }\n .pf-c-accordion__expanded-content.pf-m-fixed {\n max-height: var(--pf-c-accordion__expanded-content--m-fixed--MaxHeight);\n overflow-y: auto; }\n .pf-c-accordion__expanded-content.pf-m-expanded {\n --pf-c-accordion__expanded-content-body--before--BackgroundColor: var(--pf-c-accordion__expanded-content--m-expanded__expanded-content-body--before--BackgroundColor); }\n\n.pf-c-accordion__expanded-content-body {\n position: relative;\n padding: var(--pf-c-accordion__expanded-content-body--PaddingTop) var(--pf-c-accordion__expanded-content-body--PaddingRight) var(--pf-c-accordion__expanded-content-body--PaddingBottom) var(--pf-c-accordion__expanded-content-body--PaddingLeft); }\n .pf-c-accordion__expanded-content-body::before {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: var(--pf-c-accordion__expanded-content-body--before--Width);\n content: \"\";\n background-color: var(--pf-c-accordion__expanded-content-body--before--BackgroundColor); }\n\n.pf-c-action-list {\n --pf-c-action-list--m-icon--spacer: 0;\n --pf-c-action-list--child--spacer-base: var(--pf-global--spacer--md);\n --pf-c-action-list--group--spacer-base: var(--pf-global--spacer--2xl); }\n\n.pf-c-action-list,\n.pf-c-action-list__group {\n --pf-c-action-list--child--spacer: var(--pf-c-action-list--child--spacer-base);\n --pf-c-action-list--group--spacer: var(--pf-c-action-list--group--spacer-base);\n display: flex;\n align-items: center; }\n .pf-c-action-list > * + *,\n .pf-c-action-list__group > * + * {\n margin-left: var(--pf-c-action-list--child--spacer); }\n .pf-c-action-list > * + .pf-c-action-list__group,\n .pf-c-action-list .pf-c-action-list__group + *,\n .pf-c-action-list__group > * + .pf-c-action-list__group,\n .pf-c-action-list__group .pf-c-action-list__group + * {\n margin-left: var(--pf-c-action-list--group--spacer); }\n .pf-c-action-list.pf-m-icons,\n .pf-c-action-list__group.pf-m-icons {\n --pf-c-action-list--child--spacer: var(--pf-c-action-list--m-icon--spacer); }\n\n.pf-c-alert {\n --pf-c-alert--BoxShadow: var(--pf-global--BoxShadow--lg);\n --pf-c-alert--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-alert--GridTemplateColumns: max-content 1fr max-content;\n --pf-c-alert--BorderTopWidth: var(--pf-global--BorderWidth--md);\n --pf-c-alert--BorderTopColor: var(--pf-global--default-color--200);\n --pf-c-alert--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-alert--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-alert--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-alert--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-alert__FontSize: var(--pf-global--FontSize--sm);\n --pf-c-alert__icon--Color: var(--pf-global--default-color--200);\n --pf-c-alert__icon--MarginTop: 0.0625rem;\n --pf-c-alert__icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-alert__icon--FontSize: var(--pf-global--icon--FontSize--md);\n --pf-c-alert__title--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-alert__title--Color: var(--pf-global--default-color--300);\n --pf-c-alert__title--max-lines: 1;\n --pf-c-alert__action--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-alert__action--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-alert__action--TranslateY: 0.125rem;\n --pf-c-alert__action--MarginRight: calc(var(--pf-global--spacer--sm) * -1);\n --pf-c-alert__description--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-alert__action-group--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-alert__description--action-group--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-alert__action-group__c-button--not-last-child--MarginRight: var(--pf-global--spacer--lg);\n --pf-c-alert--m-success--BorderTopColor: var(--pf-global--success-color--100);\n --pf-c-alert--m-success__icon--Color: var(--pf-global--success-color--100);\n --pf-c-alert--m-success__title--Color: var(--pf-global--success-color--200);\n --pf-c-alert--m-danger--BorderTopColor: var(--pf-global--danger-color--100);\n --pf-c-alert--m-danger__icon--Color: var(--pf-global--danger-color--100);\n --pf-c-alert--m-danger__title--Color: var(--pf-global--danger-color--200);\n --pf-c-alert--m-warning--BorderTopColor: var(--pf-global--warning-color--100);\n --pf-c-alert--m-warning__icon--Color: var(--pf-global--warning-color--100);\n --pf-c-alert--m-warning__title--Color: var(--pf-global--warning-color--200);\n --pf-c-alert--m-info--BorderTopColor: var(--pf-global--info-color--100);\n --pf-c-alert--m-info__icon--Color: var(--pf-global--info-color--100);\n --pf-c-alert--m-info__title--Color: var(--pf-global--info-color--200);\n --pf-c-alert--m-inline--BoxShadow: none;\n --pf-c-alert--m-inline--BackgroundColor: var(--pf-global--palette--cyan-50);\n --pf-c-alert--m-inline--m-success--BackgroundColor: var(--pf-global--palette--green-50);\n --pf-c-alert--m-inline--m-danger--BackgroundColor: var(--pf-global--palette--red-50);\n --pf-c-alert--m-inline--m-warning--BackgroundColor: var(--pf-global--palette--gold-50);\n --pf-c-alert--m-inline--m-info--BackgroundColor: var(--pf-global--palette--blue-50);\n color: var(--pf-global--Color--100);\n position: relative;\n display: grid;\n padding: var(--pf-c-alert--PaddingTop) var(--pf-c-alert--PaddingRight) var(--pf-c-alert--PaddingBottom) var(--pf-c-alert--PaddingLeft);\n font-size: var(--pf-c-alert__FontSize);\n background-color: var(--pf-c-alert--BackgroundColor);\n border-top: var(--pf-c-alert--BorderTopWidth) solid var(--pf-c-alert--BorderTopColor);\n box-shadow: var(--pf-c-alert--BoxShadow);\n grid-template-columns: var(--pf-c-alert--GridTemplateColumns);\n grid-template-areas: \"icon title action\" \". description description\" \". actiongroup actiongroup\"; }\n .pf-c-alert.pf-m-success {\n --pf-c-alert--BorderTopColor: var(--pf-c-alert--m-success--BorderTopColor);\n --pf-c-alert__icon--Color: var(--pf-c-alert--m-success__icon--Color);\n --pf-c-alert__title--Color: var(--pf-c-alert--m-success__title--Color);\n --pf-c-alert--m-inline--BackgroundColor: var(--pf-c-alert--m-inline--m-success--BackgroundColor); }\n .pf-c-alert.pf-m-danger {\n --pf-c-alert--BorderTopColor: var(--pf-c-alert--m-danger--BorderTopColor);\n --pf-c-alert__icon--Color: var(--pf-c-alert--m-danger__icon--Color);\n --pf-c-alert__title--Color: var(--pf-c-alert--m-danger__title--Color);\n --pf-c-alert--m-inline--BackgroundColor: var(--pf-c-alert--m-inline--m-danger--BackgroundColor); }\n .pf-c-alert.pf-m-warning {\n --pf-c-alert--BorderTopColor: var(--pf-c-alert--m-warning--BorderTopColor);\n --pf-c-alert__icon--Color: var(--pf-c-alert--m-warning__icon--Color);\n --pf-c-alert__title--Color: var(--pf-c-alert--m-warning__title--Color);\n --pf-c-alert--m-inline--BackgroundColor: var(--pf-c-alert--m-inline--m-warning--BackgroundColor); }\n .pf-c-alert.pf-m-info {\n --pf-c-alert--BorderTopColor: var(--pf-c-alert--m-info--BorderTopColor);\n --pf-c-alert__icon--Color: var(--pf-c-alert--m-info__icon--Color);\n --pf-c-alert__title--Color: var(--pf-c-alert--m-info__title--Color);\n --pf-c-alert--m-inline--BackgroundColor: var(--pf-c-alert--m-inline--m-info--BackgroundColor); }\n .pf-c-alert.pf-m-inline {\n --pf-c-alert--BoxShadow: var(--pf-c-alert--m-inline--BoxShadow);\n --pf-c-alert--BackgroundColor: var(--pf-c-alert--m-inline--BackgroundColor); }\n\n.pf-c-alert__icon {\n grid-area: icon;\n display: flex;\n margin-top: var(--pf-c-alert__icon--MarginTop);\n margin-right: var(--pf-c-alert__icon--MarginRight);\n font-size: var(--pf-c-alert__icon--FontSize);\n color: var(--pf-c-alert__icon--Color); }\n\n.pf-c-alert__title {\n grid-area: title;\n font-weight: var(--pf-c-alert__title--FontWeight);\n color: var(--pf-c-alert__title--Color);\n word-break: break-word; }\n .pf-c-alert__title.pf-m-truncate {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: var(--pf-c-alert__title--max-lines);\n overflow: hidden; }\n\n.pf-c-alert__description {\n grid-area: description;\n padding-top: var(--pf-c-alert__description--PaddingTop);\n word-break: break-word; }\n .pf-c-alert__description + .pf-c-alert__action-group {\n --pf-c-alert__action-group--PaddingTop: var(--pf-c-alert__description--action-group--PaddingTop); }\n\n.pf-c-alert__action {\n grid-area: action;\n margin-top: var(--pf-c-alert__action--MarginTop);\n margin-right: var(--pf-c-alert__action--MarginRight);\n margin-bottom: var(--pf-c-alert__action--MarginBottom);\n transform: translateY(var(--pf-c-alert__action--TranslateY)); }\n .pf-c-alert__action > .pf-c-button {\n --pf-c-button--LineHeight: 1; }\n\n.pf-c-alert__action-group {\n grid-area: actiongroup;\n padding-top: var(--pf-c-alert__action-group--PaddingTop); }\n .pf-c-alert__action-group > .pf-c-button {\n --pf-c-button--m-link--m-inline--hover--TextDecoration: none; }\n .pf-c-alert__action-group > .pf-c-button:not(:last-child) {\n margin-right: var(--pf-c-alert__action-group__c-button--not-last-child--MarginRight); }\n\n.pf-m-overpass-font .pf-c-alert__title {\n --pf-c-alert__title--FontWeight: var(--pf-global--FontWeight--normal); }\n\n.pf-c-alert-group {\n --pf-c-alert-group__item--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-alert-group--m-toast--Top: var(--pf-global--spacer--2xl);\n --pf-c-alert-group--m-toast--Right: var(--pf-global--spacer--xl);\n --pf-c-alert-group--m-toast--MaxWidth: 37.5rem;\n --pf-c-alert-group--m-toast--ZIndex: var(--pf-global--ZIndex--2xl); }\n .pf-c-alert-group > * + * {\n margin-top: var(--pf-c-alert-group__item--MarginTop); }\n .pf-c-alert-group.pf-m-toast {\n position: fixed;\n top: var(--pf-c-alert-group--m-toast--Top);\n right: var(--pf-c-alert-group--m-toast--Right);\n z-index: var(--pf-c-alert-group--m-toast--ZIndex);\n width: calc(100% - calc(var(--pf-c-alert-group--m-toast--Right) * 2));\n max-width: var(--pf-c-alert-group--m-toast--MaxWidth); }\n\n.pf-c-app-launcher {\n --pf-c-app-launcher__menu--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-app-launcher__menu--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-app-launcher__menu--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu--Top: calc(100% + var(--pf-global--spacer--xs));\n --pf-c-app-launcher__menu--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-app-launcher--m-top__menu--Top: 0;\n --pf-c-app-launcher--m-top__menu--TranslateY: calc(-100% - var(--pf-global--spacer--xs));\n --pf-c-app-launcher__toggle--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-app-launcher__toggle--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-app-launcher__toggle--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-app-launcher__toggle--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-app-launcher__toggle--Color: var(--pf-global--Color--200);\n --pf-c-app-launcher__toggle--hover--Color: var(--pf-global--Color--100);\n --pf-c-app-launcher__toggle--active--Color: var(--pf-global--Color--100);\n --pf-c-app-launcher__toggle--focus--Color: var(--pf-global--Color--100);\n --pf-c-app-launcher__toggle--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-app-launcher__toggle--m-expanded--Color: var(--pf-global--Color--100);\n --pf-c-app-launcher__menu-search--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu-search--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-app-launcher__menu-search--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-app-launcher__menu-search--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-app-launcher__menu-search--BottomBorderColor: var(--pf-global--BorderColor--100);\n --pf-c-app-launcher__menu-search--BottomBorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-app-launcher__menu-search--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu-item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu-item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-app-launcher__menu-item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu-item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-app-launcher__menu-item--Color: var(--pf-global--Color--dark-100);\n --pf-c-app-launcher__menu-item--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-app-launcher__menu-item--Width: 100%;\n --pf-c-app-launcher__menu-item--disabled--Color: var(--pf-global--Color--dark-200);\n --pf-c-app-launcher__menu-item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-app-launcher__menu-item--m-link--PaddingRight: 0;\n --pf-c-app-launcher__menu-item--m-link--hover--BackgroundColor: transparent;\n --pf-c-app-launcher__menu-item--m-action--Color: var(--pf-global--disabled-color--200);\n --pf-c-app-launcher__menu-item--m-action--Width: auto;\n --pf-c-app-launcher__menu-item--m-action--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-app-launcher__menu-item--m-action--hover--BackgroundColor: transparent;\n --pf-c-app-launcher__menu-item--hover__menu-item--m-action--Color: var(--pf-global--Color--200);\n --pf-c-app-launcher__menu-item--m-action--hover--Color: var(--pf-global--Color--100);\n --pf-c-app-launcher__menu-item--m-favorite__menu-item--m-action--Color: var(--pf-global--palette--gold-400);\n --pf-c-app-launcher__menu-item-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__menu-item-icon--Width: var(--pf-global--icon--FontSize--lg);\n --pf-c-app-launcher__menu-item-icon--Height: var(--pf-global--icon--FontSize--lg);\n --pf-c-app-launcher__menu-item-external-icon--Color: var(--pf-global--link--Color);\n --pf-c-app-launcher__menu-item-external-icon--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-app-launcher__menu-item-external-icon--TranslateY: -0.0625rem;\n --pf-c-app-launcher__menu-item-external-icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-app-launcher__group--group--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__group-title--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-app-launcher__group-title--PaddingRight: var(--pf-c-app-launcher__menu-item--PaddingRight);\n --pf-c-app-launcher__group-title--PaddingBottom: var(--pf-c-app-launcher__menu-item--PaddingBottom);\n --pf-c-app-launcher__group-title--PaddingLeft: var(--pf-c-app-launcher__menu-item--PaddingLeft);\n --pf-c-app-launcher__group-title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-app-launcher__group-title--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-app-launcher__group-title--Color: var(--pf-global--Color--dark-200);\n --pf-c-app-launcher--c-divider--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-app-launcher--c-divider--MarginBottom: var(--pf-global--spacer--sm);\n position: relative;\n display: inline-block;\n max-width: 100%; }\n .pf-c-app-launcher.pf-m-expanded > .pf-c-app-launcher__toggle {\n color: var(--pf-c-app-launcher__toggle--m-expanded--Color); }\n .pf-c-app-launcher .pf-c-divider {\n margin-top: var(--pf-c-app-launcher--c-divider--MarginTop);\n margin-bottom: var(--pf-c-app-launcher--c-divider--MarginBottom); }\n .pf-c-app-launcher .pf-c-divider:last-child {\n --pf-c-app-launcher--c-divider--MarginBottom: 0; }\n\n.pf-c-app-launcher__toggle {\n padding: var(--pf-c-app-launcher__toggle--PaddingTop) var(--pf-c-app-launcher__toggle--PaddingRight) var(--pf-c-app-launcher__toggle--PaddingBottom) var(--pf-c-app-launcher__toggle--PaddingLeft);\n color: var(--pf-c-app-launcher__toggle--Color);\n border: none; }\n .pf-c-app-launcher__toggle:hover {\n --pf-c-app-launcher__toggle--Color: var(--pf-c-app-launcher__toggle--hover--Color); }\n .pf-c-app-launcher__toggle:active, .pf-c-app-launcher__toggle.pf-m-active {\n --pf-c-app-launcher__toggle--Color: var(--pf-c-app-launcher__toggle--active--Color); }\n .pf-c-app-launcher__toggle:focus {\n --pf-c-app-launcher__toggle--Color: var(--pf-c-app-launcher__toggle--focus--Color); }\n .pf-c-app-launcher__toggle:disabled {\n --pf-c-app-launcher__toggle--Color: var(--pf-c-app-launcher__toggle--disabled--Color);\n pointer-events: none; }\n\n.pf-c-app-launcher__menu {\n position: absolute;\n top: var(--pf-c-app-launcher__menu--Top);\n z-index: var(--pf-c-app-launcher__menu--ZIndex);\n min-width: 100%;\n padding-top: var(--pf-c-app-launcher__menu--PaddingTop);\n padding-bottom: var(--pf-c-app-launcher__menu--PaddingBottom);\n background-color: var(--pf-c-app-launcher__menu--BackgroundColor);\n background-clip: padding-box;\n box-shadow: var(--pf-c-app-launcher__menu--BoxShadow); }\n .pf-c-app-launcher__menu.pf-m-align-right {\n right: 0; }\n .pf-c-app-launcher.pf-m-top .pf-c-app-launcher__menu {\n --pf-c-app-launcher__menu--Top: var(--pf-c-app-launcher--m-top__menu--Top);\n transform: translateY(var(--pf-c-app-launcher--m-top__menu--TranslateY)); }\n\n.pf-c-app-launcher__menu-search {\n padding: var(--pf-c-app-launcher__menu-search--PaddingTop) var(--pf-c-app-launcher__menu-search--PaddingRight) var(--pf-c-app-launcher__menu-search--PaddingBottom) var(--pf-c-app-launcher__menu-search--PaddingLeft);\n margin-bottom: var(--pf-c-app-launcher__menu-search--MarginBottom);\n border-bottom: var(--pf-c-app-launcher__menu-search--BottomBorderWidth) solid var(--pf-c-app-launcher__menu-search--BottomBorderColor); }\n\n.pf-c-app-launcher__menu-wrapper {\n display: flex; }\n .pf-c-app-launcher__menu-wrapper.pf-m-favorite {\n --pf-c-app-launcher__menu-item--m-action--Color: var(--pf-c-app-launcher__menu-item--m-favorite__menu-item--m-action--Color); }\n\n.pf-c-app-launcher__menu-item {\n display: flex;\n align-items: center;\n width: var(--pf-c-app-launcher__menu-item--Width);\n padding: var(--pf-c-app-launcher__menu-item--PaddingTop) var(--pf-c-app-launcher__menu-item--PaddingRight) var(--pf-c-app-launcher__menu-item--PaddingBottom) var(--pf-c-app-launcher__menu-item--PaddingLeft);\n font-weight: var(--pf-c-app-launcher__menu-item--FontWeight);\n color: var(--pf-c-app-launcher__menu-item--Color);\n white-space: nowrap;\n border: 0; }\n .pf-c-app-launcher__menu-item:hover, .pf-c-app-launcher__menu-item:focus {\n --pf-c-app-launcher__menu-item--m-action--Color: var(--pf-c-app-launcher__menu-item--hover__menu-item--m-action--Color);\n text-decoration: none; }\n .pf-c-app-launcher__menu-wrapper:hover,\n .pf-c-app-launcher__menu-wrapper:focus-within,\n .pf-c-app-launcher__menu-wrapper.pf-m-focus, .pf-c-app-launcher__menu-item:hover, .pf-c-app-launcher__menu-item:focus {\n background-color: var(--pf-c-app-launcher__menu-item--hover--BackgroundColor); }\n .pf-c-app-launcher__menu-item:disabled, .pf-c-app-launcher__menu-item.pf-m-disabled {\n --pf-c-app-launcher__menu-item--Color: var(--pf-c-app-launcher__menu-item--disabled--Color);\n pointer-events: none; }\n .pf-c-app-launcher__menu-wrapper:disabled, .pf-c-app-launcher__menu-wrapper.pf-m-disabled, .pf-c-app-launcher__menu-item:disabled, .pf-c-app-launcher__menu-item.pf-m-disabled {\n background-color: transparent; }\n .pf-c-app-launcher__menu-wrapper.pf-m-external:hover .pf-c-app-launcher__menu-item-external-icon, .pf-c-app-launcher__menu-wrapper.pf-m-external:focus .pf-c-app-launcher__menu-item-external-icon, .pf-c-app-launcher__menu-item.pf-m-external:hover .pf-c-app-launcher__menu-item-external-icon, .pf-c-app-launcher__menu-item.pf-m-external:focus .pf-c-app-launcher__menu-item-external-icon {\n opacity: 1; }\n .pf-c-app-launcher__menu-item.pf-m-link {\n --pf-c-app-launcher__menu-item--PaddingRight: var(--pf-c-app-launcher__menu-item--m-link--PaddingRight);\n --pf-c-app-launcher__menu-item--hover--BackgroundColor: var(--pf-c-app-launcher__menu-item--m-link--hover--BackgroundColor); }\n .pf-c-app-launcher__menu-item.pf-m-action {\n --pf-c-app-launcher__menu-item--Color: var(--pf-c-app-launcher__menu-item--m-action--Color);\n --pf-c-app-launcher__menu-item--Width: var(--pf-c-app-launcher__menu-item--m-action--Width);\n --pf-c-app-launcher__menu-item--hover--BackgroundColor: var(--pf-c-app-launcher__menu-item--m-action--hover--BackgroundColor);\n font-size: var(--pf-c-app-launcher__menu-item--m-action--FontSize); }\n .pf-c-app-launcher__menu-item.pf-m-action:hover, .pf-c-app-launcher__menu-item.pf-m-action:focus {\n --pf-c-app-launcher__menu-item--m-action--Color: var(--pf-c-app-launcher__menu-item--m-action--hover--Color); }\n\n.pf-c-app-launcher__menu-item-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--pf-c-app-launcher__menu-item-icon--Width);\n height: var(--pf-c-app-launcher__menu-item-icon--Height);\n margin-right: var(--pf-c-app-launcher__menu-item-icon--MarginRight); }\n .pf-c-app-launcher__menu-item-icon > * {\n max-width: 100%;\n max-height: 100%; }\n\n.pf-c-app-launcher__menu-item-external-icon {\n padding-left: var(--pf-c-app-launcher__menu-item-external-icon--PaddingLeft);\n margin-left: auto;\n font-size: var(--pf-c-app-launcher__menu-item-external-icon--FontSize);\n color: var(--pf-c-app-launcher__menu-item-external-icon--Color);\n opacity: 0;\n transform: translateY(var(--pf-c-app-launcher__menu-item-external-icon--TranslateY)); }\n\n.pf-c-app-launcher__group + .pf-c-app-launcher__group {\n padding-top: var(--pf-c-app-launcher__group--group--PaddingTop); }\n\n.pf-c-app-launcher__group-title {\n padding-top: var(--pf-c-app-launcher__group-title--PaddingTop);\n padding-right: var(--pf-c-app-launcher__group-title--PaddingRight);\n padding-bottom: var(--pf-c-app-launcher__group-title--PaddingBottom);\n padding-left: var(--pf-c-app-launcher__group-title--PaddingLeft);\n font-size: var(--pf-c-app-launcher__group-title--FontSize);\n font-weight: var(--pf-c-app-launcher__group-title--FontWeight);\n color: var(--pf-c-app-launcher__group-title--Color); }\n\n.pf-c-avatar {\n --pf-c-avatar--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-avatar--Width: 2.25rem;\n --pf-c-avatar--Height: 2.25rem;\n width: var(--pf-c-avatar--Width);\n height: var(--pf-c-avatar--Height);\n border-radius: var(--pf-c-avatar--BorderRadius); }\n\n.pf-c-backdrop {\n --pf-c-backdrop--ZIndex: var(--pf-global--ZIndex--lg);\n --pf-c-backdrop--BackgroundColor: var(--pf-global--BackgroundColor--dark-transparent-100);\n position: fixed;\n top: 0;\n left: 0;\n z-index: var(--pf-c-backdrop--ZIndex);\n width: 100%;\n height: 100%;\n background-color: var(--pf-c-backdrop--BackgroundColor); }\n\n.pf-c-backdrop__open {\n overflow: hidden; }\n\n.pf-c-background-image {\n --pf-c-background-image--BackgroundColor: var(--pf-global--BackgroundColor--dark-100);\n --pf-c-background-image--BackgroundImage: url(\"./assets/images/pfbg_576.jpg\");\n --pf-c-background-image--BackgroundImage-2x: url(\"./assets/images/pfbg_576@2x.jpg\");\n --pf-c-background-image--BackgroundImage--sm: url(\"./assets/images/pfbg_768.jpg\");\n --pf-c-background-image--BackgroundImage--sm-2x: url(\"./assets/images/pfbg_768@2x.jpg\");\n --pf-c-background-image--BackgroundImage--lg: url(\"./assets/images/pfbg_2000.jpg\");\n --pf-c-background-image--Filter: url(\"#image_overlay\"); }\n .pf-c-background-image::before {\n position: fixed;\n top: 0;\n left: 0;\n z-index: -1;\n width: 100%;\n height: 100%;\n content: \"\";\n background-color: var(--pf-c-background-image--BackgroundColor);\n background-image: var(--pf-c-background-image--BackgroundImage);\n filter: var(--pf-c-background-image--Filter);\n background-repeat: no-repeat;\n background-size: cover; }\n @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {\n .pf-c-background-image::before {\n --pf-c-background-image--BackgroundImage: var(--pf-c-background-image--BackgroundImage-2x); } }\n @media (min-width: 576px) {\n .pf-c-background-image::before {\n --pf-c-background-image--BackgroundImage: var(--pf-c-background-image--BackgroundImage--sm); } }\n @media (min-width: 576px) and (-webkit-min-device-pixel-ratio: 2), (min-width: 576px) and (min-resolution: 192dpi) {\n .pf-c-background-image::before {\n --pf-c-background-image--BackgroundImage: var(--pf-c-background-image--BackgroundImage--sm-2x); } }\n @media (min-width: 992px) {\n .pf-c-background-image::before {\n --pf-c-background-image--BackgroundImage: var(--pf-c-background-image--BackgroundImage--lg); } }\n\n.pf-c-background-image__filter {\n display: block; }\n\n.pf-c-badge {\n --pf-c-badge--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-badge--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-badge--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-badge--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-badge--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-badge--Color: var(--pf-global--Color--dark-100);\n --pf-c-badge--MinWidth: var(--pf-global--spacer--xl);\n --pf-c-badge--m-read--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-badge--m-read--Color: var(--pf-global--Color--dark-100);\n --pf-c-badge--m-unread--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-badge--m-unread--Color: var(--pf-global--Color--light-100);\n display: inline-block;\n min-width: var(--pf-c-badge--MinWidth);\n padding-right: var(--pf-c-badge--PaddingRight);\n padding-left: var(--pf-c-badge--PaddingLeft);\n font-size: var(--pf-c-badge--FontSize);\n font-weight: var(--pf-c-badge--FontWeight);\n color: var(--pf-c-badge--Color);\n text-align: center;\n background-color: var(--pf-c-badge--BackgroundColor);\n border-radius: var(--pf-c-badge--BorderRadius); }\n .pf-c-badge.pf-m-read {\n --pf-c-badge--Color: var(--pf-c-badge--m-read--Color);\n --pf-c-badge--BackgroundColor: var(--pf-c-badge--m-read--BackgroundColor); }\n .pf-c-badge.pf-m-unread {\n --pf-c-badge--Color: var(--pf-c-badge--m-unread--Color);\n --pf-c-badge--BackgroundColor: var(--pf-c-badge--m-unread--BackgroundColor); }\n\n.pf-c-banner {\n --pf-c-banner--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-banner--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-banner--md--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-banner--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-banner--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-banner--md--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-banner--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-banner--Color: var(--pf-global--Color--100);\n --pf-c-banner--BackgroundColor: var(--pf-global--BackgroundColor--dark-400);\n --pf-c-banner--m-info--BackgroundColor: var(--pf-global--palette--blue-200);\n --pf-c-banner--m-danger--BackgroundColor: var(--pf-global--danger-color--100);\n --pf-c-banner--m-success--BackgroundColor: var(--pf-global--success-color--100);\n --pf-c-banner--m-warning--BackgroundColor: var(--pf-global--warning-color--100);\n --pf-c-banner--m-sticky--ZIndex: var(--pf-global--ZIndex--md);\n --pf-c-banner--m-sticky--BoxShadow: var(--pf-global--BoxShadow--md-bottom);\n color: var(--pf-global--Color--100);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: var(--pf-c-banner--PaddingTop) var(--pf-c-banner--PaddingRight) var(--pf-c-banner--PaddingBottom) var(--pf-c-banner--PaddingLeft);\n flex-shrink: 0;\n font-size: var(--pf-c-banner--FontSize);\n color: var(--pf-c-banner--Color);\n white-space: nowrap;\n background-color: var(--pf-c-banner--BackgroundColor); }\n @media (min-width: 768px) {\n .pf-c-banner {\n --pf-c-banner--PaddingRight: var(--pf-c-banner--md--PaddingRight);\n --pf-c-banner--PaddingLeft: var(--pf-c-banner--md--PaddingLeft); } }\n .pf-c-banner.pf-m-info {\n color: var(--pf-global--Color--100);\n --pf-c-banner--BackgroundColor: var(--pf-c-banner--m-info--BackgroundColor); }\n .pf-c-banner.pf-m-danger {\n --pf-c-banner--BackgroundColor: var(--pf-c-banner--m-danger--BackgroundColor); }\n .pf-c-banner.pf-m-success {\n --pf-c-banner--BackgroundColor: var(--pf-c-banner--m-success--BackgroundColor); }\n .pf-c-banner.pf-m-warning {\n color: var(--pf-global--Color--100);\n --pf-c-banner--BackgroundColor: var(--pf-c-banner--m-warning--BackgroundColor); }\n .pf-c-banner.pf-m-sticky {\n position: sticky;\n top: 0;\n z-index: var(--pf-c-banner--m-sticky--ZIndex);\n box-shadow: var(--pf-c-banner--m-sticky--BoxShadow); }\n\n.pf-c-breadcrumb {\n --pf-c-breadcrumb__item--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-breadcrumb__item--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-breadcrumb__item--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-breadcrumb__item-divider--Color: var(--pf-global--BorderColor--200);\n --pf-c-breadcrumb__item-divider--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-breadcrumb__item-divider--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-breadcrumb__link--m-current--Color: var(--pf-global--Color--100);\n --pf-c-breadcrumb__heading--FontSize: var(--pf-global--FontSize--sm);\n display: inline-flex; }\n\n.pf-c-breadcrumb__list {\n display: flex;\n flex-wrap: wrap;\n align-items: center; }\n\n.pf-c-breadcrumb__item {\n display: flex;\n align-items: baseline;\n font-size: var(--pf-c-breadcrumb__item--FontSize);\n font-weight: var(--pf-c-breadcrumb__item--FontWeight);\n line-height: var(--pf-c-breadcrumb__item--LineHeight);\n white-space: nowrap;\n list-style: none; }\n .pf-c-breadcrumb__item:not(:last-child) {\n margin-right: var(--pf-c-breadcrumb__item--MarginRight); }\n\n.pf-c-breadcrumb__item-divider {\n margin-right: var(--pf-c-breadcrumb__item-divider--MarginRight);\n font-size: var(--pf-c-breadcrumb__item-divider--FontSize);\n line-height: 1;\n color: var(--pf-c-breadcrumb__item-divider--Color); }\n\n.pf-c-breadcrumb__link {\n font-size: inherit;\n font-weight: var(--pf-c-breadcrumb__link--FontWeight);\n line-height: inherit;\n word-break: break-word; }\n .pf-c-breadcrumb__link.pf-m-current {\n cursor: default; }\n .pf-c-breadcrumb__link.pf-m-current, .pf-c-breadcrumb__link.pf-m-current:hover {\n color: var(--pf-c-breadcrumb__link--m-current--Color);\n text-decoration: none; }\n\n.pf-c-breadcrumb__heading {\n display: inline;\n font-size: var(--pf-c-breadcrumb__heading--FontSize); }\n\n.pf-c-breadcrumb__link,\n.pf-c-breadcrumb__heading {\n white-space: normal; }\n\n.pf-m-overpass-font .pf-c-breadcrumb__link,\n.pf-m-overpass-font .pf-c-breadcrumb__item {\n font-weight: var(--pf-global--FontWeight--semi-bold); }\n\n.pf-c-breadcrumb__list > :first-child .pf-c-breadcrumb__item-divider {\n display: none;\n visibility: hidden; }\n\n.pf-c-button {\n --pf-c-button--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-button--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-button--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-button--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-button--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-button--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-button--FontSize: var(--pf-global--FontSize--md);\n --pf-c-button--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-button--after--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-button--after--BorderColor: transparent;\n --pf-c-button--after--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-button--hover--after--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--focus--after--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--active--after--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-button--disabled--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-button--disabled--after--BorderColor: transparent;\n --pf-c-button--m-primary--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-button--m-primary--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-primary--hover--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-button--m-primary--hover--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-primary--focus--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-button--m-primary--focus--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-primary--active--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-button--m-primary--active--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-secondary--BackgroundColor: transparent;\n --pf-c-button--m-secondary--after--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--Color: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--hover--BackgroundColor: transparent;\n --pf-c-button--m-secondary--hover--after--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--hover--Color: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--focus--BackgroundColor: transparent;\n --pf-c-button--m-secondary--focus--after--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--focus--Color: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--active--BackgroundColor: transparent;\n --pf-c-button--m-secondary--active--after--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-button--m-secondary--active--Color: var(--pf-global--primary-color--100);\n --pf-c-button--m-tertiary--BackgroundColor: transparent;\n --pf-c-button--m-tertiary--after--BorderColor: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--Color: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--hover--BackgroundColor: transparent;\n --pf-c-button--m-tertiary--hover--after--BorderColor: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--hover--Color: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--focus--BackgroundColor: transparent;\n --pf-c-button--m-tertiary--focus--after--BorderColor: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--focus--Color: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--active--BackgroundColor: transparent;\n --pf-c-button--m-tertiary--active--after--BorderColor: var(--pf-global--Color--100);\n --pf-c-button--m-tertiary--active--Color: var(--pf-global--Color--100);\n --pf-c-button--m-warning--BackgroundColor: var(--pf-global--warning-color--100);\n --pf-c-button--m-warning--Color: var(--pf-global--Color--dark-100);\n --pf-c-button--m-warning--hover--BackgroundColor: var(--pf-global--palette--gold-500);\n --pf-c-button--m-warning--hover--Color: var(--pf-global--Color--dark-100);\n --pf-c-button--m-warning--focus--BackgroundColor: var(--pf-global--palette--gold-500);\n --pf-c-button--m-warning--focus--Color: var(--pf-global--Color--dark-100);\n --pf-c-button--m-warning--active--BackgroundColor: var(--pf-global--palette--gold-500);\n --pf-c-button--m-warning--active--Color: var(--pf-global--Color--dark-100);\n --pf-c-button--m-danger--BackgroundColor: var(--pf-global--danger-color--100);\n --pf-c-button--m-danger--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-danger--hover--BackgroundColor: var(--pf-global--danger-color--200);\n --pf-c-button--m-danger--hover--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-danger--focus--BackgroundColor: var(--pf-global--danger-color--200);\n --pf-c-button--m-danger--focus--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-danger--active--BackgroundColor: var(--pf-global--danger-color--200);\n --pf-c-button--m-danger--active--Color: var(--pf-global--Color--light-100);\n --pf-c-button--m-link--BackgroundColor: transparent;\n --pf-c-button--m-link--Color: var(--pf-global--link--Color);\n --pf-c-button--m-link--hover--BackgroundColor: transparent;\n --pf-c-button--m-link--hover--Color: var(--pf-global--link--Color--hover);\n --pf-c-button--m-link--focus--BackgroundColor: transparent;\n --pf-c-button--m-link--focus--Color: var(--pf-global--link--Color--hover);\n --pf-c-button--m-link--active--BackgroundColor: transparent;\n --pf-c-button--m-link--active--Color: var(--pf-global--link--Color--hover);\n --pf-c-button--m-link--disabled--BackgroundColor: transparent;\n --pf-c-button--m-link--m-inline--FontSize: inherit;\n --pf-c-button--m-link--m-inline--hover--TextDecoration: var(--pf-global--link--TextDecoration--hover);\n --pf-c-button--m-link--m-inline--hover--Color: var(--pf-global--link--Color--hover);\n --pf-c-button--m-plain--BackgroundColor: transparent;\n --pf-c-button--m-plain--Color: var(--pf-global--Color--200);\n --pf-c-button--m-plain--hover--BackgroundColor: transparent;\n --pf-c-button--m-plain--hover--Color: var(--pf-global--Color--100);\n --pf-c-button--m-plain--focus--BackgroundColor: transparent;\n --pf-c-button--m-plain--focus--Color: var(--pf-global--Color--100);\n --pf-c-button--m-plain--active--BackgroundColor: transparent;\n --pf-c-button--m-plain--active--Color: var(--pf-global--Color--100);\n --pf-c-button--m-plain--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-button--m-plain--disabled--BackgroundColor: transparent;\n --pf-c-button--m-control--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-button--m-control--Color: var(--pf-global--Color--100);\n --pf-c-button--m-control--BorderRadius: 0;\n --pf-c-button--m-control--after--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-button--m-control--after--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-button--m-control--after--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-button--m-control--after--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-button--m-control--after--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-button--m-control--disabled--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-button--m-control--hover--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-button--m-control--hover--Color: var(--pf-global--Color--100);\n --pf-c-button--m-control--hover--after--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--m-control--hover--after--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-button--m-control--active--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-button--m-control--active--Color: var(--pf-global--Color--100);\n --pf-c-button--m-control--active--after--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--m-control--active--after--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-button--m-control--focus--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-button--m-control--focus--Color: var(--pf-global--Color--100);\n --pf-c-button--m-control--focus--after--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--m-control--focus--after--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-button--m-control--m-expanded--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-button--m-control--m-expanded--Color: var(--pf-global--Color--100);\n --pf-c-button--m-control--m-expanded--after--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-button--m-control--m-expanded--after--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-button--m-small--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-button--m-display-lg--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-button--m-display-lg--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-button--m-display-lg--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-button--m-display-lg--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-button--m-display-lg--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-button--m-link--m-display-lg--FontSize: var(--pf-global--FontSize--lg);\n --pf-c-button__icon--m-start--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-button__icon--m-end--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-button__progress--width: calc(var(--pf-global--icon--FontSize--md) + var(--pf-global--spacer--sm));\n --pf-c-button__progress--Opacity: 0;\n --pf-c-button__progress--TranslateY: -50%;\n --pf-c-button__progress--Top: 50%;\n --pf-c-button__progress--Left: var(--pf-global--spacer--md);\n --pf-c-button--m-progress--TransitionProperty: padding;\n --pf-c-button--m-progress--TransitionDuration: var(--pf-global--TransitionDuration);\n --pf-c-button--m-progress--PaddingRight: calc(var(--pf-global--spacer--md) + var(--pf-c-button__progress--width) / 2);\n --pf-c-button--m-progress--PaddingLeft: calc(var(--pf-global--spacer--md) + var(--pf-c-button__progress--width) / 2);\n --pf-c-button--m-in-progress--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-button--m-in-progress--PaddingLeft: calc(var(--pf-global--spacer--md) + var(--pf-c-button__progress--width));\n position: relative;\n display: inline-block;\n padding: var(--pf-c-button--PaddingTop) var(--pf-c-button--PaddingRight) var(--pf-c-button--PaddingBottom) var(--pf-c-button--PaddingLeft);\n font-size: var(--pf-c-button--FontSize);\n font-weight: var(--pf-c-button--FontWeight);\n line-height: var(--pf-c-button--LineHeight);\n text-align: center;\n white-space: nowrap;\n user-select: none;\n border: 0;\n border-radius: var(--pf-c-button--BorderRadius); }\n .pf-c-button::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n content: \"\";\n border: var(--pf-c-button--after--BorderWidth) solid;\n border-color: var(--pf-c-button--after--BorderColor);\n border-radius: var(--pf-c-button--after--BorderRadius); }\n .pf-c-button:hover {\n --pf-c-button--after--BorderWidth: var(--pf-c-button--hover--after--BorderWidth);\n text-decoration: none; }\n .pf-c-button:focus {\n --pf-c-button--after--BorderWidth: var(--pf-c-button--focus--after--BorderWidth); }\n .pf-c-button:active, .pf-c-button.pf-m-active {\n --pf-c-button--after--BorderWidth: var(--pf-c-button--active--after--BorderWidth); }\n .pf-c-button.pf-m-block {\n display: block;\n width: 100%; }\n .pf-c-button.pf-m-small {\n --pf-c-button--FontSize: var(--pf-c-button--m-small--FontSize); }\n .pf-c-button.pf-m-primary.pf-m-display-lg, .pf-c-button.pf-m-secondary.pf-m-display-lg, .pf-c-button.pf-m-tertiary.pf-m-display-lg, .pf-c-button.pf-m-link.pf-m-display-lg {\n --pf-c-button--PaddingTop: var(--pf-c-button--m-display-lg--PaddingTop);\n --pf-c-button--PaddingRight: var(--pf-c-button--m-display-lg--PaddingRight);\n --pf-c-button--PaddingBottom: var(--pf-c-button--m-display-lg--PaddingBottom);\n --pf-c-button--PaddingLeft: var(--pf-c-button--m-display-lg--PaddingLeft);\n --pf-c-button--FontWeight: var(--pf-c-button--m-display-lg--FontWeight); }\n .pf-c-button.pf-m-primary {\n color: var(--pf-c-button--m-primary--Color);\n background-color: var(--pf-c-button--m-primary--BackgroundColor); }\n .pf-c-button.pf-m-primary:hover {\n --pf-c-button--m-primary--Color: var(--pf-c-button--m-primary--hover--Color);\n --pf-c-button--m-primary--BackgroundColor: var(--pf-c-button--m-primary--hover--BackgroundColor); }\n .pf-c-button.pf-m-primary:focus {\n --pf-c-button--m-primary--Color: var(--pf-c-button--m-primary--focus--Color);\n --pf-c-button--m-primary--BackgroundColor: var(--pf-c-button--m-primary--focus--BackgroundColor); }\n .pf-c-button.pf-m-primary:active, .pf-c-button.pf-m-primary.pf-m-active {\n --pf-c-button--m-primary--Color: var(--pf-c-button--m-primary--active--Color);\n --pf-c-button--m-primary--BackgroundColor: var(--pf-c-button--m-primary--active--BackgroundColor); }\n .pf-c-button.pf-m-secondary {\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-secondary--after--BorderColor);\n color: var(--pf-c-button--m-secondary--Color);\n background-color: var(--pf-c-button--m-secondary--BackgroundColor); }\n .pf-c-button.pf-m-secondary:hover {\n --pf-c-button--m-secondary--Color: var(--pf-c-button--m-secondary--hover--Color);\n --pf-c-button--m-secondary--BackgroundColor: var(--pf-c-button--m-secondary--hover--BackgroundColor);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-secondary--hover--after--BorderColor); }\n .pf-c-button.pf-m-secondary:focus {\n --pf-c-button--m-secondary--Color: var(--pf-c-button--m-secondary--focus--Color);\n --pf-c-button--m-secondary--BackgroundColor: var(--pf-c-button--m-secondary--focus--BackgroundColor);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-secondary--focus--after--BorderColor); }\n .pf-c-button.pf-m-secondary.pf-m-active, .pf-c-button.pf-m-secondary:active {\n --pf-c-button--m-secondary--Color: var(--pf-c-button--m-secondary--active--Color);\n --pf-c-button--m-secondary--BackgroundColor: var(--pf-c-button--m-secondary--active--BackgroundColor);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-secondary--active--after--BorderColor); }\n .pf-c-button.pf-m-tertiary {\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-tertiary--after--BorderColor);\n color: var(--pf-c-button--m-tertiary--Color);\n background-color: var(--pf-c-button--m-tertiary--BackgroundColor); }\n .pf-c-button.pf-m-tertiary:hover {\n --pf-c-button--m-tertiary--Color: var(--pf-c-button--m-tertiary--hover--Color);\n --pf-c-button--m-tertiary--BackgroundColor: var(--pf-c-button--m-tertiary--hover--BackgroundColor);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-tertiary--hover--after--BorderColor); }\n .pf-c-button.pf-m-tertiary:focus {\n --pf-c-button--m-tertiary--Color: var(--pf-c-button--m-tertiary--focus--Color);\n --pf-c-button--m-tertiary--BackgroundColor: var(--pf-c-button--m-tertiary--focus--BackgroundColor);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-tertiary--focus--after--BorderColor); }\n .pf-c-button.pf-m-tertiary:active, .pf-c-button.pf-m-tertiary.pf-m-active {\n --pf-c-button--m-tertiary--Color: var(--pf-c-button--m-tertiary--active--Color);\n --pf-c-button--m-tertiary--BackgroundColor: var(--pf-c-button--m-tertiary--active--BackgroundColor);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-tertiary--active--after--BorderColor); }\n .pf-c-button.pf-m-danger {\n color: var(--pf-c-button--m-danger--Color);\n background-color: var(--pf-c-button--m-danger--BackgroundColor); }\n .pf-c-button.pf-m-danger:hover {\n --pf-c-button--m-danger--Color: var(--pf-c-button--m-danger--hover--Color);\n --pf-c-button--m-danger--BackgroundColor: var(--pf-c-button--m-danger--hover--BackgroundColor); }\n .pf-c-button.pf-m-danger:focus {\n --pf-c-button--m-danger--Color: var(--pf-c-button--m-danger--focus--Color);\n --pf-c-button--m-danger--BackgroundColor: var(--pf-c-button--m-danger--focus--BackgroundColor); }\n .pf-c-button.pf-m-danger:active, .pf-c-button.pf-m-danger.pf-m-active {\n --pf-c-button--m-danger--Color: var(--pf-c-button--m-danger--active--Color);\n --pf-c-button--m-danger--BackgroundColor: var(--pf-c-button--m-danger--active--BackgroundColor); }\n .pf-c-button.pf-m-warning {\n color: var(--pf-c-button--m-warning--Color);\n background-color: var(--pf-c-button--m-warning--BackgroundColor); }\n .pf-c-button.pf-m-warning:hover {\n --pf-c-button--m-warning--Color: var(--pf-c-button--m-warning--hover--Color);\n --pf-c-button--m-warning--BackgroundColor: var(--pf-c-button--m-warning--hover--BackgroundColor); }\n .pf-c-button.pf-m-warning:focus {\n --pf-c-button--m-warning--Color: var(--pf-c-button--m-warning--focus--Color);\n --pf-c-button--m-warning--BackgroundColor: var(--pf-c-button--m-warning--focus--BackgroundColor); }\n .pf-c-button.pf-m-warning:active, .pf-c-button.pf-m-warning.pf-m-active {\n --pf-c-button--m-warning--Color: var(--pf-c-button--m-warning--active--Color);\n --pf-c-button--m-warning--BackgroundColor: var(--pf-c-button--m-warning--active--BackgroundColor); }\n .pf-c-button.pf-m-link {\n --pf-c-button--disabled--BackgroundColor: var(--pf-c-button--m-link--disabled--BackgroundColor);\n color: var(--pf-c-button--m-link--Color);\n background-color: var(--pf-c-button--m-link--BackgroundColor); }\n .pf-c-button.pf-m-link:not(.pf-m-inline):hover {\n --pf-c-button--m-link--Color: var(--pf-c-button--m-link--hover--Color);\n --pf-c-button--m-link--BackgroundColor: var(--pf-c-button--m-link--hover--BackgroundColor); }\n .pf-c-button.pf-m-link:not(.pf-m-inline):focus {\n --pf-c-button--m-link--Color: var(--pf-c-button--m-link--focus--Color);\n --pf-c-button--m-link--BackgroundColor: var(--pf-c-button--m-link--focus--BackgroundColor); }\n .pf-c-button.pf-m-link:not(.pf-m-inline):active, .pf-c-button.pf-m-link:not(.pf-m-inline).pf-m-active {\n --pf-c-button--m-link--Color: var(--pf-c-button--m-link--active--Color);\n --pf-c-button--m-link--BackgroundColor: var(--pf-c-button--m-link--active--BackgroundColor); }\n .pf-c-button.pf-m-link.pf-m-inline {\n --pf-c-button--FontSize: var(--pf-c-button--m-link--m-inline--FontSize);\n display: inline;\n padding: 0;\n text-align: left;\n white-space: normal;\n cursor: pointer; }\n .pf-c-button.pf-m-link.pf-m-inline:hover {\n --pf-c-button--m-link--Color: var(--pf-c-button--m-link--m-inline--hover--Color);\n text-decoration: var(--pf-c-button--m-link--m-inline--hover--TextDecoration); }\n .pf-c-button.pf-m-link.pf-m-display-lg {\n --pf-c-button--FontSize: var(--pf-c-button--m-link--m-display-lg--FontSize); }\n .pf-c-button.pf-m-control {\n --pf-c-button--BorderRadius: var(--pf-c-button--m-control--BorderRadius);\n --pf-c-button--disabled--BackgroundColor: var(--pf-c-button--m-control--disabled--BackgroundColor);\n --pf-c-button--after--BorderWidth: var(--pf-c-button--m-control--after--BorderWidth);\n --pf-c-button--after--BorderColor: var(--pf-c-button--m-control--after--BorderTopColor) var(--pf-c-button--m-control--after--BorderRightColor) var(--pf-c-button--m-control--after--BorderBottomColor) var(--pf-c-button--m-control--after--BorderLeftColor);\n color: var(--pf-c-button--m-control--Color);\n background-color: var(--pf-c-button--m-control--BackgroundColor); }\n .pf-c-button.pf-m-control::after {\n border-radius: initial; }\n .pf-c-button.pf-m-control:hover {\n --pf-c-button--m-control--Color: var(--pf-c-button--m-control--hover--Color);\n --pf-c-button--m-control--BackgroundColor: var(--pf-c-button--m-control--hover--BackgroundColor);\n --pf-c-button--m-control--after--BorderBottomColor: var(--pf-c-button--m-control--hover--after--BorderBottomColor); }\n .pf-c-button.pf-m-control:hover::after {\n border-bottom-width: var(--pf-c-button--m-control--hover--after--BorderBottomWidth); }\n .pf-c-button.pf-m-control:active, .pf-c-button.pf-m-control.pf-m-active {\n --pf-c-button--m-control--Color: var(--pf-c-button--m-control--active--Color);\n --pf-c-button--m-control--BackgroundColor: var(--pf-c-button--m-control--active--BackgroundColor);\n --pf-c-button--m-control--after--BorderBottomColor: var(--pf-c-button--m-control--active--after--BorderBottomColor); }\n .pf-c-button.pf-m-control:active::after, .pf-c-button.pf-m-control.pf-m-active::after {\n border-bottom-width: var(--pf-c-button--m-control--active--after--BorderBottomWidth); }\n .pf-c-button.pf-m-control:focus {\n --pf-c-button--m-control--Color: var(--pf-c-button--m-control--focus--Color);\n --pf-c-button--m-control--BackgroundColor: var(--pf-c-button--m-control--focus--BackgroundColor);\n --pf-c-button--m-control--after--BorderBottomColor: var(--pf-c-button--m-control--focus--after--BorderBottomColor); }\n .pf-c-button.pf-m-control:focus::after {\n border-bottom-width: var(--pf-c-button--m-control--focus--after--BorderBottomWidth); }\n .pf-c-button.pf-m-control.pf-m-expanded {\n --pf-c-button--m-control--Color: var(--pf-c-button--m-control--m-expanded--Color);\n --pf-c-button--m-control--BackgroundColor: var(--pf-c-button--m-control--m-expanded--BackgroundColor);\n --pf-c-button--m-control--after--BorderBottomColor: var(--pf-c-button--m-control--m-expanded--after--BorderBottomColor); }\n .pf-c-button.pf-m-control.pf-m-expanded::after {\n border-bottom-width: var(--pf-c-button--m-control--m-expanded--after--BorderBottomWidth); }\n .pf-c-button.pf-m-plain {\n --pf-c-button--disabled--Color: var(--pf-c-button--m-plain--disabled--Color);\n --pf-c-button--disabled--BackgroundColor: var(--pf-c-button--m-plain--disabled--BackgroundColor);\n color: var(--pf-c-button--m-plain--Color);\n background-color: var(--pf-c-button--m-plain--BackgroundColor); }\n .pf-c-button.pf-m-plain:hover {\n --pf-c-button--m-plain--Color: var(--pf-c-button--m-plain--hover--Color);\n --pf-c-button--m-plain--BackgroundColor: var(--pf-c-button--m-plain--hover--BackgroundColor); }\n .pf-c-button.pf-m-plain:active, .pf-c-button.pf-m-plain.pf-m-active {\n --pf-c-button--m-plain--Color: var(--pf-c-button--m-plain--active--Color);\n --pf-c-button--m-plain--BackgroundColor: var(--pf-c-button--m-plain--active--BackgroundColor); }\n .pf-c-button.pf-m-plain:focus {\n --pf-c-button--m-plain--Color: var(--pf-c-button--m-plain--focus--Color);\n --pf-c-button--m-plain--BackgroundColor: var(--pf-c-button--m-plain--focus--BackgroundColor); }\n .pf-c-button:disabled, .pf-c-button.pf-m-disabled {\n pointer-events: none; }\n .pf-c-button:disabled, .pf-c-button.pf-m-disabled, .pf-c-button.pf-m-aria-disabled {\n --pf-c-button--after--BorderColor: var(--pf-c-button--disabled--after--BorderColor);\n color: var(--pf-c-button--disabled--Color);\n background-color: var(--pf-c-button--disabled--BackgroundColor); }\n .pf-c-button.pf-m-aria-disabled {\n --pf-c-button--after--BorderWidth: 0;\n --pf-c-button--m-link--m-inline--hover--TextDecoration: none;\n cursor: default; }\n .pf-c-button.pf-m-progress {\n --pf-c-button--PaddingRight: var(--pf-c-button--m-progress--PaddingRight);\n --pf-c-button--PaddingLeft: var(--pf-c-button--m-progress--PaddingLeft);\n transition: var(--pf-c-button--m-progress--TransitionProperty) var(--pf-c-button--m-progress--TransitionDuration); }\n .pf-c-button.pf-m-in-progress {\n --pf-c-button--PaddingRight: var(--pf-c-button--m-in-progress--PaddingRight);\n --pf-c-button--PaddingLeft: var(--pf-c-button--m-in-progress--PaddingLeft); }\n\n.pf-c-button__icon.pf-m-start {\n margin-right: var(--pf-c-button__icon--m-start--MarginRight); }\n\n.pf-c-button__icon.pf-m-end {\n margin-left: var(--pf-c-button__icon--m-end--MarginLeft); }\n\n.pf-c-button__progress {\n position: absolute;\n top: var(--pf-c-button__progress--Top);\n left: var(--pf-c-button__progress--Left);\n line-height: 1;\n transform: translateY(var(--pf-c-button__progress--TranslateY)); }\n .pf-c-button__progress .pf-c-spinner {\n --pf-c-spinner--Color: currentColor; }\n\n.pf-m-overpass-font .pf-c-button {\n --pf-c-button--FontWeight: var(--pf-global--FontWeight--semi-bold); }\n\n.pf-c-calendar-month {\n --pf-c-calendar-month--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-calendar-month--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-calendar-month--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-calendar-month--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-calendar-month--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-calendar-month--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-calendar-month__header--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-calendar-month__header-year--Width: 8ch;\n --pf-c-calendar-month__header-nav-control--MarginRight: 0;\n --pf-c-calendar-month__header-nav-control--MarginLeft: 0;\n --pf-c-calendar-month__header-nav-control--m-prev-month--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-calendar-month__header-nav-control--m-prev-month--MarginLeft: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-calendar-month__header-nav-control--m-next-month--MarginRight: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-calendar-month__header-nav-control--m-next-month--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-calendar-month__days--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-calendar-month__days--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-calendar-month__day--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-calendar-month__day--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-calendar-month__dates-cell--PaddingTop: 0.125rem;\n --pf-c-calendar-month__dates-cell--PaddingRight: 0.125rem;\n --pf-c-calendar-month__dates-cell--PaddingBottom: 0.125rem;\n --pf-c-calendar-month__dates-cell--PaddingLeft: 0.125rem;\n --pf-c-calendar-month__dates-row--first-child__dates-cell--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-calendar-month__dates-cell--m-current__date--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-calendar-month__dates-cell--m-selected__date--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-calendar-month__dates-cell--m-selected__date--hover--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-calendar-month__dates-cell--m-selected__date--focus--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-calendar-month__dates-cell--m-selected__date--focus--after--BorderColor: var(--pf-global--primary-color--200);\n --pf-c-calendar-month__date-cell--m-selected__date--focus--BoxShadow: 0 0 0.3125rem var(--pf-global--primary-color--100);\n --pf-c-calendar-month__dates-cell--m-selected__date--Color: var(--pf-global--Color--light-100);\n --pf-c-calendar-month__dates-cell--before--BackgroundColor: transparent;\n --pf-c-calendar-month__dates-cell--before--Top: 0;\n --pf-c-calendar-month__dates-cell--before--Right: 0;\n --pf-c-calendar-month__dates-cell--before--Bottom: var(--pf-c-calendar-month__dates-cell--PaddingBottom);\n --pf-c-calendar-month__dates-cell--before--Left: 0;\n --pf-c-calendar-month__dates-cell--m-in-range--before--BackgroundColor: var(--pf-global--palette--blue-50);\n --pf-c-calendar-month__dates-cell--m-in-range--m-start-range--before--Left: 50%;\n --pf-c-calendar-month__dates-cell--m-in-range--m-end-range--before--Right: 50%;\n --pf-c-calendar-month__dates-cell--m-in-range__date--hover--BackgroundColor: var(--pf-global--palette--blue-100);\n --pf-c-calendar-month__dates-cell--m-in-range__date--focus--BackgroundColor: var(--pf-global--palette--blue-100);\n --pf-c-calendar-month__dates-cell--m-adjacent-month__date--Color: var(--pf-global--disabled-color--100);\n --pf-c-calendar-month__date--Width: 4ch;\n --pf-c-calendar-month__date--Height: 4ch;\n --pf-c-calendar-month__date--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-calendar-month__date--Color: var(--pf-global--Color--100);\n --pf-c-calendar-month__date--BackgroundColor: transparent;\n --pf-c-calendar-month__date--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-calendar-month__date--after--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-calendar-month__date--after--BorderColor: transparent;\n --pf-c-calendar-month__date--hover--BackgroundColor: var(--pf-global--palette--blue-50);\n --pf-c-calendar-month__date--focus--BackgroundColor: var(--pf-global--palette--blue-50);\n --pf-c-calendar-month__date--focus--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-calendar-month__date--focus--BoxShadow: none;\n color: var(--pf-global--Color--100);\n display: inline-flex;\n flex-direction: column;\n padding: var(--pf-c-calendar-month--PaddingTop) var(--pf-c-calendar-month--PaddingRight) var(--pf-c-calendar-month--PaddingBottom) var(--pf-c-calendar-month--PaddingLeft);\n font-size: var(--pf-c-calendar-month--FontSize);\n background-color: var(--pf-c-calendar-month--BackgroundColor); }\n\n.pf-c-calendar-month__header {\n display: flex;\n margin-bottom: var(--pf-c-calendar-month__header--MarginBottom); }\n\n.pf-c-calendar-month__header-nav-control {\n margin-right: var(--pf-c-calendar-month__header-nav-control--MarginRight);\n margin-left: var(--pf-c-calendar-month__header-nav-control--MarginLeft); }\n .pf-c-calendar-month__header-nav-control.pf-m-prev-month {\n --pf-c-calendar-month__header-nav-control--MarginRight: var(--pf-c-calendar-month__header-nav-control--m-prev-month--MarginRight);\n --pf-c-calendar-month__header-nav-control--MarginLeft: var(--pf-c-calendar-month__header-nav-control--m-prev-month--MarginLeft); }\n .pf-c-calendar-month__header-nav-control.pf-m-next-month {\n --pf-c-calendar-month__header-nav-control--MarginRight: var(--pf-c-calendar-month__header-nav-control--m-next-month--MarginRight);\n --pf-c-calendar-month__header-nav-control--MarginLeft: var(--pf-c-calendar-month__header-nav-control--m-next-month--MarginLeft); }\n\n.pf-c-calendar-month__header-month {\n flex-grow: 1; }\n\n.pf-c-calendar-month__header-year {\n width: var(--pf-c-calendar-month__header-year--Width); }\n\n.pf-c-calendar-month__calendar {\n table-layout: fixed; }\n\n.pf-c-calendar-month__days {\n border-bottom: var(--pf-c-calendar-month__days--BorderBottomWidth) solid var(--pf-c-calendar-month__days--BorderBottomColor); }\n\n.pf-c-calendar-month__day {\n padding-bottom: var(--pf-c-calendar-month__day--PaddingBottom);\n font-weight: var(--pf-c-calendar-month__day--FontWeight);\n text-align: center; }\n\n.pf-c-calendar-month__dates-row:first-child {\n --pf-c-calendar-month__dates-cell--PaddingTop: var(--pf-c-calendar-month__dates-row--first-child__dates-cell--PaddingTop); }\n\n.pf-c-calendar-month__dates-cell {\n --pf-c-calendar-month__dates-cell--before--Top: var(--pf-c-calendar-month__dates-cell--PaddingTop);\n position: relative;\n padding: var(--pf-c-calendar-month__dates-cell--PaddingTop) var(--pf-c-calendar-month__dates-cell--PaddingRight) var(--pf-c-calendar-month__dates-cell--PaddingBottom) var(--pf-c-calendar-month__dates-cell--PaddingLeft);\n text-align: center; }\n .pf-c-calendar-month__dates-cell::before {\n position: absolute;\n top: var(--pf-c-calendar-month__dates-cell--before--Top);\n right: var(--pf-c-calendar-month__dates-cell--before--Right);\n bottom: var(--pf-c-calendar-month__dates-cell--before--Bottom);\n left: var(--pf-c-calendar-month__dates-cell--before--Left);\n content: \"\";\n background-color: var(--pf-c-calendar-month__dates-cell--before--BackgroundColor); }\n .pf-c-calendar-month__dates-cell.pf-m-current {\n --pf-c-calendar-month__date--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-current__date--BackgroundColor); }\n .pf-c-calendar-month__dates-cell.pf-m-in-range {\n --pf-c-calendar-month__dates-cell--before--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-in-range--before--BackgroundColor);\n --pf-c-calendar-month__date--hover--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-in-range__date--hover--BackgroundColor);\n --pf-c-calendar-month__date--focus--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-in-range__date--focus--BackgroundColor); }\n .pf-c-calendar-month__dates-cell.pf-m-start-range {\n --pf-c-calendar-month__dates-cell--before--Left: var(--pf-c-calendar-month__dates-cell--m-in-range--m-start-range--before--Left); }\n .pf-c-calendar-month__dates-cell.pf-m-end-range {\n --pf-c-calendar-month__dates-cell--before--Right: var(--pf-c-calendar-month__dates-cell--m-in-range--m-end-range--before--Right); }\n .pf-c-calendar-month__dates-cell.pf-m-adjacent-month {\n --pf-c-calendar-month__date--Color: var(--pf-c-calendar-month__dates-cell--m-adjacent-month__date--Color); }\n .pf-c-calendar-month__dates-cell.pf-m-selected {\n --pf-c-calendar-month__date--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-selected__date--BackgroundColor);\n --pf-c-calendar-month__date--hover--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-selected__date--hover--BackgroundColor);\n --pf-c-calendar-month__date--focus--BackgroundColor: var(--pf-c-calendar-month__dates-cell--m-selected__date--focus--BackgroundColor);\n --pf-c-calendar-month__date--focus--after--BorderColor: var(--pf-c-calendar-month__dates-cell--m-selected__date--focus--after--BorderColor);\n --pf-c-calendar-month__date--focus--BoxShadow: var(--pf-c-calendar-month__date-cell--m-selected__date--focus--BoxShadow);\n --pf-c-calendar-month__date--Color: var(--pf-c-calendar-month__dates-cell--m-selected__date--Color); }\n .pf-c-calendar-month__dates-cell.pf-m-disabled {\n --pf-c-calendar-month__dates-cell--before--BackgroundColor: transparent;\n --pf-c-calendar-month__date--BackgroundColor: transparent; }\n\n.pf-c-calendar-month__date {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--pf-c-calendar-month__date--Width);\n height: var(--pf-c-calendar-month__date--Height);\n line-height: 1;\n color: var(--pf-c-calendar-month__date--Color);\n background-color: var(--pf-c-calendar-month__date--BackgroundColor);\n border: 0; }\n .pf-c-calendar-month__date::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-calendar-month__date--after--BorderWidth) solid var(--pf-c-calendar-month__date--after--BorderColor); }\n .pf-c-calendar-month__date, .pf-c-calendar-month__date::after {\n border-radius: var(--pf-c-calendar-month__date--BorderRadius); }\n .pf-c-calendar-month__date:hover, .pf-c-calendar-month__date.pf-m-hover {\n --pf-c-calendar-month__date--BackgroundColor: var(--pf-c-calendar-month__date--hover--BackgroundColor); }\n .pf-c-calendar-month__date:focus, .pf-c-calendar-month__date.pf-m-focus {\n --pf-c-calendar-month__date--BackgroundColor: var(--pf-c-calendar-month__date--focus--BackgroundColor);\n --pf-c-calendar-month__date--after--BorderColor: var(--pf-c-calendar-month__date--focus--after--BorderColor);\n outline: 0;\n box-shadow: var(--pf-c-calendar-month__date--focus--BoxShadow); }\n .pf-c-calendar-month__date:disabled {\n pointer-events: none;\n --pf-c-calendar-month__date--Color: var(--pf-c-calendar-month__date--disabled--Color);\n --pf-c-calendar-month__date--hover--focus--BorderColor: transparent; }\n\n.pf-c-card {\n --pf-c-card--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-card--BoxShadow: var(--pf-global--BoxShadow--sm);\n --pf-c-card--m-hoverable--hover--BoxShadow: var(--pf-global--BoxShadow--lg);\n --pf-c-card--m-selectable--hover--BoxShadow: var(--pf-global--BoxShadow--lg);\n --pf-c-card--m-selectable--focus--BoxShadow: var(--pf-global--BoxShadow--lg);\n --pf-c-card--m-selectable--active--BoxShadow: var(--pf-global--BoxShadow--lg);\n --pf-c-card--m-selectable--m-selected--BoxShadow: var(--pf-global--BoxShadow--lg);\n --pf-c-card--m-selectable--m-selected--before--Height: var(--pf-global--BorderWidth--lg);\n --pf-c-card--m-selectable--m-selected--before--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-card--m-compact__body--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-card--m-compact__footer--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-card--m-compact--first-child--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-card--m-compact--child--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-card--m-compact--child--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-card--m-compact--child--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-card--m-compact--c-divider--child--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-card--m-compact__title--not--last-child--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-card--m-flat--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-card--m-flat--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-card--first-child--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-card--child--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-card--child--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-card--child--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-card--c-divider--child--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-card__header-toggle--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-card__header-toggle--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-card__header-toggle--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-card__header-toggle--MarginLeft: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-card__header-toggle-icon--Transition: var(--pf-global--Transition);\n --pf-c-card--m-expanded__header-toggle-icon--Rotate: 90deg;\n --pf-c-card__title--FontSize: var(--pf-global--FontSize--md);\n --pf-c-card__title--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-card__title--not--last-child--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-card__body--FontSize: var(--pf-global--FontSize--md);\n --pf-c-card__footer--FontSize: var(--pf-global--FontSize--md);\n --pf-c-card__actions--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-card__actions--child--MarginLeft: var(--pf-global--spacer--sm);\n display: flex;\n flex-direction: column;\n background-color: var(--pf-c-card--BackgroundColor);\n box-shadow: var(--pf-c-card--BoxShadow); }\n .pf-c-card.pf-m-hoverable:hover {\n box-shadow: var(--pf-c-card--m-hoverable--hover--BoxShadow); }\n .pf-c-card.pf-m-selectable {\n position: relative;\n cursor: pointer; }\n .pf-c-card.pf-m-selectable:hover {\n box-shadow: var(--pf-c-card--m-selectable--hover--BoxShadow); }\n .pf-c-card.pf-m-selectable:focus {\n box-shadow: var(--pf-c-card--m-selectable--focus--BoxShadow); }\n .pf-c-card.pf-m-selectable:active {\n box-shadow: var(--pf-c-card--m-selectable--active--BoxShadow); }\n .pf-c-card.pf-m-selectable.pf-m-selected {\n box-shadow: var(--pf-c-card--m-selectable--m-selected--BoxShadow); }\n .pf-c-card.pf-m-selectable.pf-m-selected::before {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n height: var(--pf-c-card--m-selectable--m-selected--before--Height);\n content: \"\";\n background-color: var(--pf-c-card--m-selectable--m-selected--before--BackgroundColor); }\n .pf-c-card.pf-m-compact {\n --pf-c-card__body--FontSize: var(--pf-c-card--m-compact__body--FontSize);\n --pf-c-card__footer--FontSize: var(--pf-c-card--m-compact__footer--FontSize);\n --pf-c-card--first-child--PaddingTop: var(--pf-c-card--m-compact--first-child--PaddingTop);\n --pf-c-card--child--PaddingRight: var(--pf-c-card--m-compact--child--PaddingRight);\n --pf-c-card--child--PaddingBottom: var(--pf-c-card--m-compact--child--PaddingBottom);\n --pf-c-card--child--PaddingLeft: var(--pf-c-card--m-compact--child--PaddingLeft);\n --pf-c-card--c-divider--child--PaddingTop: var(--pf-c-card--m-compact--c-divider--child--PaddingTop);\n --pf-c-card__title--not--last-child--PaddingBottom: var(--pf-c-card--m-compact__title--not--last-child--PaddingBottom); }\n .pf-c-card.pf-m-flat {\n --pf-c-card--BoxShadow: none;\n border: var(--pf-c-card--m-flat--BorderWidth) solid var(--pf-c-card--m-flat--BorderColor); }\n .pf-c-card.pf-m-expanded .pf-c-card__header-toggle-icon {\n transform: rotate(var(--pf-c-card--m-expanded__header-toggle-icon--Rotate)); }\n .pf-c-card > .pf-c-divider + .pf-c-card__header,\n .pf-c-card > .pf-c-divider + .pf-c-card__title,\n .pf-c-card > .pf-c-divider + .pf-c-card__body,\n .pf-c-card > .pf-c-divider + .pf-c-card__footer {\n padding-top: var(--pf-c-card--c-divider--child--PaddingTop); }\n\n.pf-c-card__header {\n display: flex;\n flex-direction: row;\n align-items: center; }\n .pf-c-card__header .pf-c-card__title {\n padding: 0; }\n\n.pf-c-card__header-toggle {\n align-self: flex-start;\n margin: var(--pf-c-card__header-toggle--MarginTop) var(--pf-c-card__header-toggle--MarginRight) var(--pf-c-card__header-toggle--MarginBottom) var(--pf-c-card__header-toggle--MarginLeft); }\n\n.pf-c-card__header-toggle-icon {\n display: inline-block;\n transition: var(--pf-c-card__header-toggle-icon--Transition); }\n\n.pf-c-card__title {\n font-family: var(--pf-c-card__title--FontFamily);\n font-weight: var(--pf-c-card__title--FontWeight); }\n\n.pf-c-card__actions {\n display: flex;\n align-items: center;\n align-self: flex-start;\n order: 1;\n padding-left: var(--pf-c-card__actions--PaddingLeft);\n margin: var(--pf-c-card__header-toggle--MarginTop) var(--pf-c-card__header-toggle--MarginRight) var(--pf-c-card__header-toggle--MarginBottom) auto; }\n .pf-c-card__actions > * + * {\n margin-left: var(--pf-c-card__actions--child--MarginLeft); }\n .pf-c-card__actions + .pf-c-card__title,\n .pf-c-card__actions + .pf-c-card__body,\n .pf-c-card__actions + .pf-c-card__footer {\n padding: 0; }\n\n.pf-c-card__header,\n.pf-c-card__title,\n.pf-c-card__body,\n.pf-c-card__footer {\n padding-right: var(--pf-c-card--child--PaddingRight);\n padding-bottom: var(--pf-c-card--child--PaddingBottom);\n padding-left: var(--pf-c-card--child--PaddingLeft); }\n .pf-c-card__header:first-child,\n .pf-c-card__title:first-child,\n .pf-c-card__body:first-child,\n .pf-c-card__footer:first-child {\n padding-top: var(--pf-c-card--first-child--PaddingTop); }\n\n.pf-c-card__header:not(:last-child),\n.pf-c-card__title:not(:last-child) {\n padding-bottom: var(--pf-c-card__title--not--last-child--PaddingBottom); }\n\n.pf-c-card__expandable-content {\n --pf-c-card--first-child--PaddingTop: 0; }\n\n.pf-c-card__body:not(.pf-m-no-fill) {\n flex: 1 1 auto; }\n\n.pf-c-card__body {\n font-size: var(--pf-c-card__body--FontSize); }\n\n.pf-c-card__footer {\n font-size: var(--pf-c-card__footer--FontSize); }\n\n.pf-m-overpass-font .pf-c-card .pf-c-card__title {\n font-weight: var(--pf-global--FontWeight--normal); }\n\n.pf-c-check {\n --pf-c-check--GridGap: var(--pf-global--spacer--xs) var(--pf-global--spacer--sm);\n --pf-c-check__label--disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-check__label--Color: var(--pf-global--Color--100);\n --pf-c-check__label--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-check__label--FontSize: var(--pf-global--FontSize--md);\n --pf-c-check__label--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-check__input--MarginTop: -0.1875rem;\n --pf-c-check__description--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-check__description--Color: var(--pf-global--Color--200);\n display: grid;\n grid-template-columns: auto 1fr;\n grid-gap: var(--pf-c-check--GridGap);\n align-items: center;\n justify-items: start; }\n\n.pf-c-check__label {\n font-size: var(--pf-c-check__label--FontSize);\n font-weight: var(--pf-c-check__label--FontWeight);\n line-height: var(--pf-c-check__label--LineHeight);\n color: var(--pf-c-check__label--Color); }\n\n.pf-c-check__input {\n margin-top: var(--pf-c-check__input--MarginTop); }\n\n.pf-c-check__description {\n grid-column: 2;\n font-size: var(--pf-c-check__description--FontSize);\n color: var(--pf-c-check__description--Color); }\n\nlabel.pf-c-check, .pf-c-check__label,\n.pf-c-check__input {\n cursor: pointer; }\n\n.pf-c-check__label:disabled, .pf-c-check__label.pf-m-disabled,\n.pf-c-check__input:disabled,\n.pf-c-check__input.pf-m-disabled {\n --pf-c-check__label--Color: var(--pf-c-check__label--disabled--Color);\n cursor: not-allowed; }\n\n.pf-c-chip {\n --pf-c-chip--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-chip--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-chip--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-chip--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-chip--BackgroundColor: var(--pf-global--Color--light-100);\n --pf-c-chip--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-chip--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-chip--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-chip--before--BorderRadius: var(--pf-c-chip--BorderRadius);\n --pf-c-chip--m-overflow__text--Color: var(--pf-global--primary-color--100);\n --pf-c-chip--m-draggable--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-chip--m-draggable--BoxShadow: var(--pf-global--BoxShadow--sm);\n --pf-c-chip--m-draggable__icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-chip__text--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-chip__text--Color: var(--pf-global--Color--100);\n --pf-c-chip__text--MaxWidth: 16ch;\n --pf-c-chip__c-button--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-chip__c-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-chip__c-button--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-chip__c-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-chip__c-button--MarginTop: calc(var(--pf-c-chip--PaddingTop) * -1);\n --pf-c-chip__c-button--MarginRight: calc(var(--pf-c-chip--PaddingRight) / 2 * -1);\n --pf-c-chip__c-button--MarginBottom: calc(var(--pf-c-chip--PaddingBottom) * -1);\n --pf-c-chip__c-button--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-chip__c-badge--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-chip__icon--MarginLeft: var(--pf-global--spacer--sm);\n color: var(--pf-global--Color--100);\n position: relative;\n display: inline-flex;\n align-items: center;\n padding: var(--pf-c-chip--PaddingTop) var(--pf-c-chip--PaddingRight) var(--pf-c-chip--PaddingBottom) var(--pf-c-chip--PaddingLeft);\n list-style: none;\n background-color: var(--pf-c-chip--BackgroundColor);\n border-radius: var(--pf-c-chip--BorderRadius); }\n .pf-c-chip::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-chip--before--BorderWidth) solid var(--pf-c-chip--before--BorderColor);\n border-radius: var(--pf-c-chip--before--BorderRadius); }\n .pf-c-chip.pf-m-overflow {\n border: 0; }\n .pf-c-chip.pf-m-overflow .pf-c-chip__text {\n color: var(--pf-c-chip--m-overflow__text--Color); }\n .pf-c-chip.pf-m-draggable {\n --pf-c-chip--BackgroundColor: var(--pf-c-chip--m-draggable--BackgroundColor);\n box-shadow: var(--pf-c-chip--m-draggable--BoxShadow); }\n .pf-c-chip.pf-m-draggable .pf-c-chip__icon {\n font-size: var(--pf-c-chip--m-draggable__icon--FontSize); }\n .pf-c-chip .pf-c-button {\n --pf-c-button--PaddingTop: var(--pf-c-chip__c-button--PaddingTop);\n --pf-c-button--PaddingRight: var(--pf-c-chip__c-button--PaddingRight);\n --pf-c-button--PaddingBottom: var(--pf-c-chip__c-button--PaddingBottom);\n --pf-c-button--PaddingLeft: var(--pf-c-chip__c-button--PaddingLeft);\n --pf-c-button--FontSize: var(--pf-c-chip__c-button--FontSize);\n margin-top: var(--pf-c-chip__c-button--MarginTop);\n margin-right: var(--pf-c-chip__c-button--MarginRight);\n margin-bottom: var(--pf-c-chip__c-button--MarginBottom); }\n .pf-c-chip .pf-c-badge {\n margin-left: var(--pf-c-chip__c-badge--MarginLeft); }\n\n.pf-c-chip__text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n position: relative;\n max-width: var(--pf-c-chip__text--MaxWidth);\n font-size: var(--pf-c-chip__text--FontSize);\n color: var(--pf-c-chip__text--Color); }\n\n.pf-c-chip__icon + .pf-c-chip__text,\n.pf-c-chip__text + .pf-c-chip__icon {\n margin-left: var(--pf-c-chip__icon--MarginLeft); }\n\n.pf-c-chip-group {\n color: var(--pf-global--Color--100);\n --pf-c-chip-group__list--MarginBottom: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-chip-group__list--MarginRight: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-chip-group--m-category--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-chip-group--m-category--PaddingRight: var(--pf-global--spacer--xs);\n --pf-c-chip-group--m-category--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-chip-group--m-category--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-chip-group--m-category--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-chip-group--m-category--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-chip-group__label--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-chip-group__label--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-chip-group__label--MaxWidth: 18ch;\n --pf-c-chip-group__close--MarginTop: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-chip-group__close--MarginBottom: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-chip-group__list-item--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-chip-group__list-item--MarginBottom: var(--pf-global--spacer--xs); }\n .pf-c-chip-group.pf-m-category {\n padding-top: var(--pf-c-chip-group--m-category--PaddingTop);\n padding-right: var(--pf-c-chip-group--m-category--PaddingRight);\n padding-bottom: var(--pf-c-chip-group--m-category--PaddingBottom);\n padding-left: var(--pf-c-chip-group--m-category--PaddingLeft);\n background-color: var(--pf-c-chip-group--m-category--BackgroundColor);\n border-radius: var(--pf-c-chip-group--m-category--BorderRadius); }\n\n.pf-c-chip-group__main {\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n align-items: baseline; }\n\n.pf-c-chip-group__list {\n margin-right: var(--pf-c-chip-group__list--MarginRight);\n margin-bottom: var(--pf-c-chip-group__list--MarginBottom); }\n\n.pf-c-chip-group,\n.pf-c-chip-group__list {\n display: inline-flex;\n flex-wrap: wrap;\n align-items: center; }\n\n.pf-c-chip-group__list-item {\n display: inline-flex;\n margin-right: var(--pf-c-chip-group__list-item--MarginRight);\n margin-bottom: var(--pf-c-chip-group__list-item--MarginBottom); }\n\n.pf-c-chip-group__label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: var(--pf-c-chip-group__label--MaxWidth);\n margin-right: var(--pf-c-chip-group__label--MarginRight);\n font-size: var(--pf-c-chip-group__label--FontSize); }\n\n.pf-c-chip-group__close {\n display: flex;\n align-self: flex-start;\n margin-top: var(--pf-c-chip-group__close--MarginTop);\n margin-bottom: var(--pf-c-chip-group__close--MarginBottom); }\n\n.pf-c-clipboard-copy {\n --pf-c-clipboard-copy__toggle-icon--Transition: .2s ease-in 0s;\n --pf-c-clipboard-copy--m-expanded__toggle-icon--Rotate: 90deg;\n --pf-c-clipboard-copy__expandable-content--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-clipboard-copy__expandable-content--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-clipboard-copy__expandable-content--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-clipboard-copy__expandable-content--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-clipboard-copy__expandable-content--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-clipboard-copy__expandable-content--BorderTopWidth: 0;\n --pf-c-clipboard-copy__expandable-content--BorderRightWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-clipboard-copy__expandable-content--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-clipboard-copy__expandable-content--BorderLeftWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-clipboard-copy__expandable-content--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-clipboard-copy__expandable-content--OutlineOffset: calc(-1 * var(--pf-global--spacer--xs)); }\n .pf-c-clipboard-copy.pf-m-expanded .pf-c-clipboard-copy__toggle-icon {\n transform: rotate(var(--pf-c-clipboard-copy--m-expanded__toggle-icon--Rotate)); }\n\n.pf-c-clipboard-copy__group {\n display: flex; }\n .pf-c-clipboard-copy__group > * + * {\n margin-left: -1px; }\n\n.pf-c-clipboard-copy__toggle-icon {\n transition: var(--pf-c-clipboard-copy__toggle-icon--Transition); }\n\n.pf-c-clipboard-copy__expandable-content {\n padding: var(--pf-c-clipboard-copy__expandable-content--PaddingTop) var(--pf-c-clipboard-copy__expandable-content--PaddingRight) var(--pf-c-clipboard-copy__expandable-content--PaddingBottom) var(--pf-c-clipboard-copy__expandable-content--PaddingLeft);\n word-wrap: break-word;\n background-color: var(--pf-c-clipboard-copy__expandable-content--BackgroundColor);\n background-clip: padding-box;\n border: solid var(--pf-c-clipboard-copy__expandable-content--BorderColor);\n border-width: var(--pf-c-clipboard-copy__expandable-content--BorderTopWidth) var(--pf-c-clipboard-copy__expandable-content--BorderRightWidth) var(--pf-c-clipboard-copy__expandable-content--BorderBottomWidth) var(--pf-c-clipboard-copy__expandable-content--BorderLeftWidth);\n box-shadow: var(--pf-c-clipboard-copy__expandable-content--BoxShadow); }\n .pf-c-clipboard-copy__expandable-content pre {\n white-space: pre-wrap; }\n\n.pf-c-code-editor {\n --pf-c-code-editor__controls--c-button--m-control--Color: var(--pf-global--Color--200);\n --pf-c-code-editor__controls--c-button--m-control--hover--Color: var(--pf-global--Color--100);\n --pf-c-code-editor__controls--c-button--m-control--focus--Color: var(--pf-global--Color--100);\n --pf-c-code-editor__controls--c-button--m-control--disabled--after--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-code-editor__header--before--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-code-editor__header--before--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-code-editor__main--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-code-editor__main--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-code-editor__main--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-code-editor--m-read-only__main--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-code-editor__main--m-drag-hover--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-code-editor__main--m-drag-hover--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-code-editor__main--m-drag-hover--after--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-code-editor__main--m-drag-hover--after--Opacity: .1;\n --pf-c-code-editor__code--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-code-editor__code--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-code-editor__code--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-code-editor__code--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-code-editor__code-pre--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-code-editor__code-pre--FontFamily: var(--pf-global--FontFamily--monospace);\n --pf-c-code-editor__tab--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-code-editor__tab--Color: var(--pf-global--Color--200);\n --pf-c-code-editor__tab--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-code-editor__tab--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-code-editor__tab--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-code-editor__tab--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-code-editor__tab--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-code-editor__tab--BorderRightWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-code-editor__tab--BorderBottomWidth: 0;\n --pf-c-code-editor__tab--BorderLeftWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-code-editor__tab--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-code-editor__tab-icon--text--MarginLeft: var(--pf-global--spacer--sm); }\n .pf-c-code-editor.pf-m-read-only {\n --pf-c-code-editor__main--BackgroundColor: var(--pf-c-code-editor--m-read-only__main--BackgroundColor); }\n\n.pf-c-code-editor__header {\n position: relative;\n display: flex;\n align-items: flex-end; }\n .pf-c-code-editor__header::before {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n content: \"\";\n border-bottom: var(--pf-c-code-editor__header--before--BorderBottomWidth) solid var(--pf-c-code-editor__header--before--BorderBottomColor); }\n\n.pf-c-code-editor__controls {\n display: flex; }\n .pf-c-code-editor__controls .pf-c-button.pf-m-control {\n --pf-c-button--m-control--Color: var(--pf-c-code-editor__controls--c-button--m-control--Color); }\n .pf-c-code-editor__controls .pf-c-button.pf-m-control:hover {\n --pf-c-code-editor__controls--c-button--m-control--Color: var(--pf-c-code-editor__controls--c-button--m-control--hover--Color); }\n .pf-c-code-editor__controls .pf-c-button.pf-m-control:focus {\n --pf-c-code-editor__controls--c-button--m-control--Color: var(--pf-c-code-editor__controls--c-button--m-control--focus--Color); }\n .pf-c-code-editor__controls .pf-c-button.pf-m-control:disabled::after {\n border-bottom-color: var(--pf-c-code-editor__controls--c-button--m-control--disabled--after--BorderBottomColor); }\n\n.pf-c-code-editor__main {\n position: relative;\n background-color: var(--pf-c-code-editor__main--BackgroundColor);\n border: var(--pf-c-code-editor__main--BorderWidth) solid;\n border-color: var(--pf-c-code-editor__main--BorderColor); }\n .pf-c-code-editor__main.pf-m-drag-hover::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n background-color: var(--pf-c-code-editor__main--m-drag-hover--after--BackgroundColor);\n opacity: var(--pf-c-code-editor__main--m-drag-hover--after--Opacity); }\n .pf-c-code-editor__main.pf-m-drag-hover::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-code-editor__main--m-drag-hover--before--BorderWidth) solid var(--pf-c-code-editor__main--m-drag-hover--before--BorderColor); }\n .pf-c-code-editor__main .monaco-editor {\n background-color: var(--pf-c-code-editor__main--BackgroundColor); }\n\n.pf-c-code-editor__header + .pf-c-code-editor__main {\n border-top-width: 0; }\n\n.pf-c-code-editor__code {\n position: relative;\n padding: var(--pf-c-code-editor__code--PaddingTop) var(--pf-c-code-editor__code--PaddingRight) var(--pf-c-code-editor__code--PaddingBottom) var(--pf-c-code-editor__code--PaddingLeft); }\n .pf-c-code-editor__code .pf-c-code-editor__code-pre {\n font-family: var(--pf-c-code-editor__code-pre--FontFamily);\n font-size: var(--pf-c-code-editor__code-pre--FontSize);\n white-space: pre-wrap; }\n\n.pf-c-code-editor__tab {\n position: relative;\n display: flex;\n align-items: center;\n padding: var(--pf-c-code-editor__tab--PaddingTop) var(--pf-c-code-editor__tab--PaddingRight) var(--pf-c-code-editor__tab--PaddingBottom) var(--pf-c-code-editor__tab--PaddingLeft);\n margin-left: auto;\n color: var(--pf-c-code-editor__tab--Color);\n background-color: var(--pf-c-code-editor__tab--BackgroundColor);\n border-color: var(--pf-c-code-editor__tab--BorderColor);\n border-style: solid;\n border-width: var(--pf-c-code-editor__tab--BorderTopWidth) var(--pf-c-code-editor__tab--BorderRightWidth) var(--pf-c-code-editor__tab--BorderBottomWidth) var(--pf-c-code-editor__tab--BorderLeftWidth); }\n\n.pf-c-code-editor__tab-icon + .pf-c-code-editor__tab-text {\n margin-left: var(--pf-c-code-editor__tab-icon--text--MarginLeft); }\n\n.pf-c-content {\n --pf-c-content--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-content--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--FontSize: var(--pf-global--FontSize--md);\n --pf-c-content--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--Color: var(--pf-global--Color--100);\n --pf-c-content--heading--FontFamily: var(--pf-global--FontFamily--heading--sans-serif);\n --pf-c-content--h1--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-content--h1--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-content--h1--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-content--h1--FontSize: var(--pf-global--FontSize--2xl);\n --pf-c-content--h1--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--h2--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-content--h2--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-content--h2--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--h2--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-content--h2--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--h3--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-content--h3--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-content--h3--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--h3--FontSize: var(--pf-global--FontSize--lg);\n --pf-c-content--h3--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--h4--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-content--h4--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-content--h4--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--h4--FontSize: var(--pf-global--FontSize--md);\n --pf-c-content--h4--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--h5--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-content--h5--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-content--h5--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--h5--FontSize: var(--pf-global--FontSize--md);\n --pf-c-content--h5--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--h6--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-content--h6--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-content--h6--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--h6--FontSize: var(--pf-global--FontSize--md);\n --pf-c-content--h6--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-content--small--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-content--small--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-content--small--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-content--small--Color: var(--pf-global--Color--200);\n --pf-c-content--a--Color: var(--pf-global--link--Color);\n --pf-c-content--a--TextDecoration: var(--pf-global--link--TextDecoration);\n --pf-c-content--a--hover--Color: var(--pf-global--link--Color--hover);\n --pf-c-content--a--hover--TextDecoration: var(--pf-global--link--TextDecoration--hover);\n --pf-c-content--blockquote--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-content--blockquote--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-content--blockquote--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-content--blockquote--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-content--blockquote--Color: var(--pf-global--Color--200);\n --pf-c-content--blockquote--BorderLeftColor: var(--pf-global--BorderColor--100);\n --pf-c-content--blockquote--BorderLeftWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-content--ol--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-content--ol--MarginLeft: var(--pf-global--spacer--lg);\n --pf-c-content--ol--nested--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-content--ol--nested--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-content--ul--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-content--ul--MarginLeft: var(--pf-global--spacer--lg);\n --pf-c-content--ul--nested--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-content--ul--nested--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-content--ul--ListStyle: var(--pf-global--ListStyle);\n --pf-c-content--li--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-content--dl--ColumnGap: var(--pf-global--spacer--2xl);\n --pf-c-content--dl--RowGap: var(--pf-global--spacer--md);\n --pf-c-content--dt--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-content--dt--MarginTop: var(--pf-global--spacer--md);\n --pf-c-content--dt--sm--MarginTop: 0;\n --pf-c-content--hr--Height: var(--pf-global--BorderWidth--sm);\n --pf-c-content--hr--BackgroundColor: var(--pf-global--BorderColor--100);\n font-size: var(--pf-c-content--FontSize);\n line-height: var(--pf-c-content--LineHeight);\n color: var(--pf-c-content--Color); }\n .pf-c-content a {\n color: var(--pf-c-content--a--Color);\n text-decoration: var(--pf-c-content--a--TextDecoration); }\n .pf-c-content a:hover {\n --pf-c-content--a--Color: var(--pf-c-content--a--hover--Color);\n --pf-c-content--a--TextDecoration: var(--pf-c-content--a--hover--TextDecoration); }\n .pf-c-content li + li {\n margin-top: var(--pf-c-content--li--MarginTop); }\n .pf-c-content p:not(:last-child),\n .pf-c-content dl:not(:last-child),\n .pf-c-content ol:not(:last-child),\n .pf-c-content ul:not(:last-child),\n .pf-c-content blockquote:not(:last-child),\n .pf-c-content small:not(:last-child),\n .pf-c-content pre:not(:last-child),\n .pf-c-content table:not(:last-child),\n .pf-c-content hr:not(:last-child) {\n margin-bottom: var(--pf-c-content--MarginBottom); }\n .pf-c-content h1,\n .pf-c-content h2,\n .pf-c-content h3,\n .pf-c-content h4,\n .pf-c-content h5,\n .pf-c-content h6 {\n margin: 0;\n font-family: var(--pf-c-content--heading--FontFamily); }\n .pf-c-content h1:first-child,\n .pf-c-content h2:first-child,\n .pf-c-content h3:first-child,\n .pf-c-content h4:first-child,\n .pf-c-content h5:first-child,\n .pf-c-content h6:first-child {\n margin-top: 0; }\n .pf-c-content h1:last-child,\n .pf-c-content h2:last-child,\n .pf-c-content h3:last-child,\n .pf-c-content h4:last-child,\n .pf-c-content h5:last-child,\n .pf-c-content h6:last-child {\n margin-bottom: 0; }\n .pf-c-content ol,\n .pf-c-content ul {\n margin: 0; }\n .pf-c-content h1 {\n margin-top: var(--pf-c-content--h1--MarginTop);\n margin-bottom: var(--pf-c-content--h1--MarginBottom);\n font-size: var(--pf-c-content--h1--FontSize);\n font-weight: var(--pf-c-content--h1--FontWeight);\n line-height: var(--pf-c-content--h1--LineHeight); }\n .pf-c-content h2 {\n margin-top: var(--pf-c-content--h2--MarginTop);\n margin-bottom: var(--pf-c-content--h2--MarginBottom);\n font-size: var(--pf-c-content--h2--FontSize);\n font-weight: var(--pf-c-content--h2--FontWeight);\n line-height: var(--pf-c-content--h2--LineHeight); }\n .pf-c-content h3 {\n margin-top: var(--pf-c-content--h3--MarginTop);\n margin-bottom: var(--pf-c-content--h3--MarginBottom);\n font-size: var(--pf-c-content--h3--FontSize);\n font-weight: var(--pf-c-content--h3--FontWeight);\n line-height: var(--pf-c-content--h3--LineHeight); }\n .pf-c-content h4 {\n margin-top: var(--pf-c-content--h4--MarginTop);\n margin-bottom: var(--pf-c-content--h4--MarginBottom);\n font-size: var(--pf-c-content--h4--FontSize);\n font-weight: var(--pf-c-content--h4--FontWeight);\n line-height: var(--pf-c-content--h4--LineHeight); }\n .pf-c-content h5 {\n margin-top: var(--pf-c-content--h5--MarginTop);\n margin-bottom: var(--pf-c-content--h5--MarginBottom);\n font-size: var(--pf-c-content--h5--FontSize);\n font-weight: var(--pf-c-content--h5--FontWeight);\n line-height: var(--pf-c-content--h5--LineHeight); }\n .pf-c-content h6 {\n margin-top: var(--pf-c-content--h6--MarginTop);\n margin-bottom: var(--pf-c-content--h6--MarginBottom);\n font-size: var(--pf-c-content--h6--FontSize);\n font-weight: var(--pf-c-content--h6--FontWeight);\n line-height: var(--pf-c-content--h6--LineHeight); }\n .pf-c-content small {\n display: block;\n font-size: var(--pf-c-content--small--FontSize);\n line-height: var(--pf-c-content--small--LineHeight);\n color: var(--pf-c-content--small--Color); }\n .pf-c-content small:not(:last-child) {\n margin-bottom: var(--pf-c-content--small--MarginBottom); }\n .pf-c-content blockquote {\n padding: var(--pf-c-content--blockquote--PaddingTop) var(--pf-c-content--blockquote--PaddingRight) var(--pf-c-content--blockquote--PaddingBottom) var(--pf-c-content--blockquote--PaddingLeft);\n color: var(--pf-c-content--blockquote--Color);\n border-left: var(--pf-c-content--blockquote--BorderLeftWidth) solid var(--pf-c-content--blockquote--BorderLeftColor); }\n .pf-c-content hr {\n height: var(--pf-c-content--hr--Height);\n background-color: var(--pf-c-content--hr--BackgroundColor);\n border: none; }\n .pf-c-content ol {\n padding-left: var(--pf-c-content--ol--PaddingLeft);\n margin-left: var(--pf-c-content--ol--MarginLeft); }\n .pf-c-content ol ul {\n margin-top: var(--pf-c-content--ul--nested--MarginTop);\n --pf-c-content--ul--MarginLeft: var(--pf-c-content--ul--nested--MarginLeft); }\n .pf-c-content ol ol {\n margin-top: var(--pf-c-content--ol--nested--MarginTop);\n --pf-c-content--ol--MarginLeft: var(--pf-c-content--ol--nested--MarginLeft); }\n .pf-c-content ul {\n padding-left: var(--pf-c-content--ul--PaddingLeft);\n margin-left: var(--pf-c-content--ul--MarginLeft);\n list-style: var(--pf-c-content--ul--ListStyle); }\n .pf-c-content ul ul {\n margin-top: var(--pf-c-content--ul--nested--MarginTop);\n --pf-c-content--ul--MarginLeft: var(--pf-c-content--ul--nested--MarginLeft); }\n .pf-c-content ul ol {\n margin-top: var(--pf-c-content--ol--nested--MarginTop);\n --pf-c-content--ol--MarginLeft: var(--pf-c-content--ol--nested--MarginLeft); }\n .pf-c-content dl {\n display: grid;\n grid-template-columns: 1fr; }\n @media screen and (min-width: 576px) {\n .pf-c-content dl {\n grid-template: auto / auto 1fr;\n grid-column-gap: var(--pf-c-content--dl--ColumnGap);\n grid-row-gap: var(--pf-c-content--dl--RowGap); } }\n .pf-c-content dt {\n font-weight: var(--pf-c-content--dt--FontWeight); }\n .pf-c-content dt:not(:first-child) {\n margin-top: var(--pf-c-content--dt--MarginTop); }\n @media screen and (min-width: 576px) {\n .pf-c-content dt:not(:first-child) {\n --pf-c-content--dt--MarginTop: var(--pf-c-content--dt--sm--MarginTop); } }\n @media screen and (min-width: 576px) {\n .pf-c-content dt {\n grid-column: 1; } }\n @media screen and (min-width: 576px) {\n .pf-c-content dd {\n grid-column: 2; } }\n\n.pf-m-overpass-font .pf-c-content {\n --pf-c-content--h2--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-content--h4--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-content--h5--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-content--h6--FontWeight: var(--pf-global--FontWeight--semi-bold); }\n .pf-m-overpass-font .pf-c-content blockquote {\n font-weight: var(--pf-global--FontWeight--light); }\n\n.pf-c-context-selector {\n --pf-c-context-selector--Width: 15.625rem;\n --pf-c-context-selector__toggle--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-context-selector__toggle--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-context-selector__toggle--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-context-selector__toggle--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-context-selector__toggle--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-context-selector__toggle--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-context-selector__toggle--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-context-selector__toggle--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-context-selector__toggle--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-context-selector__toggle--Color: var(--pf-global--Color--100);\n --pf-c-context-selector__toggle--hover--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-context-selector__toggle--active--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-context-selector__toggle--active--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-context-selector__toggle--expanded--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-context-selector__toggle--expanded--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-context-selector__toggle-text--FontSize: var(--pf-global--FontSize--md);\n --pf-c-context-selector__toggle-text--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-context-selector__toggle-text--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-context-selector__toggle-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-context-selector__toggle-icon--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu--Top: calc(100% + var(--pf-global--spacer--xs));\n --pf-c-context-selector__menu--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-context-selector__menu--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-context-selector__menu--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-context-selector__menu--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-context-selector__menu-search--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-context-selector__menu-search--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-search--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-search--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-search--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-context-selector__menu-search--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-context-selector__menu-footer--BoxShadow: var(--pf-global--BoxShadow--sm-top);\n --pf-c-context-selector__menu-footer--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-footer--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-footer--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-footer--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-context-selector__menu-list--MaxHeight: 12.5rem;\n --pf-c-context-selector__menu-list-item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-context-selector__menu-list-item--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-context-selector__menu-list-item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-context-selector__menu-list-item--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-context-selector__menu-list-item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-context-selector__menu-list-item--disabled--Color: var(--pf-global--Color--dark-200);\n position: relative;\n display: inline-block;\n width: var(--pf-c-context-selector--Width);\n max-width: 100%; }\n\n.pf-c-context-selector__toggle {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: var(--pf-c-context-selector__toggle--PaddingTop) var(--pf-c-context-selector__toggle--PaddingRight) var(--pf-c-context-selector__toggle--PaddingBottom) var(--pf-c-context-selector__toggle--PaddingLeft);\n color: var(--pf-c-context-selector__toggle--Color);\n white-space: nowrap;\n cursor: pointer;\n border: none; }\n .pf-c-context-selector__toggle::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-context-selector__toggle--BorderWidth) solid;\n border-color: var(--pf-c-context-selector__toggle--BorderTopColor) var(--pf-c-context-selector__toggle--BorderRightColor) var(--pf-c-context-selector__toggle--BorderBottomColor) var(--pf-c-context-selector__toggle--BorderLeftColor); }\n .pf-c-context-selector__toggle:hover::before {\n --pf-c-context-selector__toggle--BorderBottomColor: var(--pf-c-context-selector__toggle--hover--BorderBottomColor); }\n .pf-c-context-selector__toggle:active::before, .pf-c-context-selector__toggle.pf-m-active::before, .pf-c-context-selector__toggle:focus-within::before {\n --pf-c-context-selector__toggle--BorderBottomColor: var(--pf-c-context-selector__toggle--active--BorderBottomColor);\n border-bottom-width: var(--pf-c-context-selector__toggle--active--BorderBottomWidth); }\n .pf-m-expanded > .pf-c-context-selector__toggle::before {\n --pf-c-context-selector__toggle--BorderBottomColor: var(--pf-c-context-selector__toggle--expanded--BorderBottomColor);\n border-bottom-width: var(--pf-c-context-selector__toggle--expanded--BorderBottomWidth); }\n .pf-c-context-selector__toggle .pf-c-context-selector__toggle-icon {\n margin-right: var(--pf-c-context-selector__toggle-icon--MarginRight);\n margin-left: var(--pf-c-context-selector__toggle-icon--MarginLeft); }\n .pf-c-context-selector__toggle .pf-c-context-selector__toggle-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: var(--pf-c-context-selector__toggle-text--FontSize);\n font-weight: var(--pf-c-context-selector__toggle-text--FontWeight);\n line-height: var(--pf-c-context-selector__toggle-text--LineHeight); }\n\n.pf-c-context-selector__menu {\n color: var(--pf-global--Color--100);\n position: absolute;\n top: var(--pf-c-context-selector__menu--Top);\n z-index: var(--pf-c-context-selector__menu--ZIndex);\n min-width: 100%;\n padding-top: var(--pf-c-context-selector__menu--PaddingTop);\n background-color: var(--pf-c-context-selector__menu--BackgroundColor);\n background-clip: padding-box;\n box-shadow: var(--pf-c-context-selector__menu--BoxShadow); }\n\n.pf-c-context-selector__menu-search {\n position: relative;\n padding: var(--pf-c-context-selector__menu-search--PaddingTop) var(--pf-c-context-selector__menu-search--PaddingRight) var(--pf-c-context-selector__menu-search--PaddingBottom) var(--pf-c-context-selector__menu-search--PaddingLeft);\n border-bottom: var(--pf-c-context-selector__menu-search--BorderBottomWidth) solid var(--pf-c-context-selector__menu-search--BorderBottomColor); }\n\n.pf-c-context-selector__menu-footer {\n padding: var(--pf-c-context-selector__menu-footer--PaddingTop) var(--pf-c-context-selector__menu-footer--PaddingRight) var(--pf-c-context-selector__menu-footer--PaddingBottom) var(--pf-c-context-selector__menu-footer--PaddingLeft);\n text-align: right;\n box-shadow: var(--pf-c-context-selector__menu-footer--BoxShadow); }\n\n.pf-c-context-selector__menu-list {\n max-height: var(--pf-c-context-selector__menu-list--MaxHeight);\n overflow-y: scroll; }\n\n.pf-c-context-selector__menu-list-item {\n display: flex;\n align-items: center;\n width: 100%;\n padding: var(--pf-c-context-selector__menu-list-item--PaddingTop) var(--pf-c-context-selector__menu-list-item--PaddingRight) var(--pf-c-context-selector__menu-list-item--PaddingBottom) var(--pf-c-context-selector__menu-list-item--PaddingLeft);\n white-space: nowrap;\n border: none; }\n .pf-c-context-selector__menu-list-item:hover, .pf-c-context-selector__menu-list-item:focus {\n text-decoration: none;\n background-color: var(--pf-c-context-selector__menu-list-item--hover--BackgroundColor); }\n .pf-c-context-selector__menu-list-item:disabled {\n color: var(--pf-c-context-selector__menu-list-item--disabled--Color);\n pointer-events: none; }\n\n@media screen and (min-width: 768px) {\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 768px) and (min-width: 1200px) {\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 768px) {\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list:not([class*=\"pf-m-grid\"]) .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n@media screen and (min-width: 0) {\n .pf-c-data-list.pf-m-grid-none {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 0) and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-none {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 0) {\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list.pf-m-grid-none .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n@media screen and (min-width: 576px) {\n .pf-c-data-list.pf-m-grid-sm {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 576px) and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-sm {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 576px) {\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list.pf-m-grid-sm .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n@media screen and (min-width: 768px) {\n .pf-c-data-list.pf-m-grid-md {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 768px) and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-md {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 768px) {\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list.pf-m-grid-md .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n@media screen and (min-width: 992px) {\n .pf-c-data-list.pf-m-grid-lg {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 992px) and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-lg {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 992px) {\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list.pf-m-grid-lg .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n@media screen and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-xl {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 1200px) and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-xl {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list.pf-m-grid-xl .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n@media screen and (min-width: 1450px) {\n .pf-c-data-list.pf-m-grid-2xl {\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list__cell--cell--md--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list__cell--md--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list__item-control--md--MarginRight);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list__item-action--md--MarginLeft);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-c-data-list__expandable-content-body--md--PaddingTop);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-c-data-list__expandable-content-body--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--md--PaddingBottom);\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--md--PaddingTop); } }\n @media screen and (min-width: 1450px) and (min-width: 1200px) {\n .pf-c-data-list.pf-m-grid-2xl {\n --pf-c-data-list__item-row--PaddingRight: var(--pf-c-data-list__item-row--xl--PaddingRight);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-c-data-list__item-row--xl--PaddingLeft);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-c-data-list__expandable-content-body--xl--PaddingRight);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-c-data-list__expandable-content-body--xl--PaddingLeft); } }\n\n@media screen and (min-width: 1450px) {\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__item-content {\n display: flex;\n flex-wrap: wrap;\n flex-grow: 1;\n padding-bottom: var(--pf-c-data-list__item-content--md--PaddingBottom); }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell:not(:last-child):not(.pf-m-icon) {\n margin-right: var(--pf-c-data-list__cell--MarginRight); }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1;\n order: initial; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-align-right {\n margin-left: auto; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-no-fill {\n flex-grow: 0; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-2 {\n flex-grow: 2; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-3 {\n flex-grow: 3; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-4 {\n flex-grow: 4; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__cell.pf-m-flex-5 {\n flex-grow: 5; }\n .pf-c-data-list.pf-m-grid-2xl .pf-c-data-list__expandable-content {\n max-height: initial;\n overflow-y: visible; } }\n\n.pf-c-data-list {\n --pf-c-data-list--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-data-list--BorderTopWidth: var(--pf-global--spacer--sm);\n --pf-c-data-list--sm--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-data-list--sm--BorderTopColor: var(--pf-global--BorderColor--100);\n --pf-c-data-list__item--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-data-list__item--m-selected--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-data-list__item--m-expanded--before--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-data-list__item--m-selected--before--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-data-list__item--m-selected--BoxShadow: var(--pf-global--BoxShadow--sm-top), var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-data-list__item--m-selectable--OutlineOffset: calc(-1 * var(--pf-global--spacer--xs));\n --pf-c-data-list__item--m-selectable--hover--ZIndex: calc(var(--pf-c-data-list__item--m-selected--ZIndex) + 1);\n --pf-c-data-list__item--m-selectable--hover--BoxShadow: var(--pf-global--BoxShadow--sm-top), var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-data-list__item--m-selectable--focus--BoxShadow: var(--pf-global--BoxShadow--sm-top), var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-data-list__item--m-selectable--active--BoxShadow: var(--pf-global--BoxShadow--sm-top), var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-data-list__item--m-expanded--m-selectable--before--BackgroundColor: var(--pf-global--active-color--300);\n --pf-c-data-list__item--BorderBottomColor: var(--pf-global--BorderColor--300);\n --pf-c-data-list__item--BorderBottomWidth: 0.5rem;\n --pf-c-data-list__item--m-selectable--hover--item--BorderTopColor: var(--pf-c-data-list__item--BorderBottomColor);\n --pf-c-data-list__item--m-selectable--hover--item--BorderTopWidth: var(--pf-c-data-list__item--BorderBottomWidth);\n --pf-c-data-list__item--sm--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-data-list__item--sm--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-data-list__item--before--BackgroundColor: transparent;\n --pf-c-data-list__item--before--Width: var(--pf-global--BorderWidth--lg);\n --pf-c-data-list__item--before--Transition: var(--pf-global--Transition);\n --pf-c-data-list__item--before--Top: 0;\n --pf-c-data-list__item--before--sm--Top: calc(var(--pf-c-data-list__item--BorderBottomWidth) * -1);\n --pf-c-data-list__item-row--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-data-list__item-row--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-data-list__item-row--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-data-list__item-row--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-data-list__item-content--md--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-data-list__cell--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-data-list__cell--MarginRight: var(--pf-global--spacer--xl);\n --pf-c-data-list__cell--md--PaddingBottom: 0;\n --pf-c-data-list__cell--m-icon--MarginRight: var(--pf-global--spacer--md);\n --pf-c-data-list__cell--cell--PaddingTop: 0;\n --pf-c-data-list__cell--cell--md--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-data-list__cell--m-icon--cell--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-data-list--cell--MinWidth: initial;\n --pf-c-data-list--cell--Overflow: visible;\n --pf-c-data-list--cell--TextOverflow: clip;\n --pf-c-data-list--cell--WhiteSpace: normal;\n --pf-c-data-list--cell--WordBreak: normal;\n --pf-c-data-list--cell--m-truncate--MinWidth: 5ch;\n --pf-c-data-list__toggle--MarginLeft: calc(var(--pf-global--spacer--sm) * -1);\n --pf-c-data-list__toggle--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-data-list__toggle-icon--Transition: .2s ease-in 0s;\n --pf-c-data-list__item--m-expanded__toggle-icon--Rotate: 90deg;\n --pf-c-data-list__item-draggable-button--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-data-list__item-draggable-button--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-data-list__item-draggable-button--MarginTop: calc(var(--pf-global--spacer--sm) * -1);\n --pf-c-data-list__item-draggable-button--MarginLeft: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-data-list__item-draggable-button-icon--Color: var(--pf-global--icon--Color--light);\n --pf-c-data-list__item-draggable-button--m-disabled__draggable-icon--Color: var(--pf-global--disabled-color--200);\n --pf-c-data-list__item-draggable-button--hover__draggable-icon--Color: var(--pf-global--icon--Color--dark);\n --pf-c-data-list__item-draggable-button--focus__draggable-icon--Color: var(--pf-global--icon--Color--dark);\n --pf-c-data-list__item--m-ghost-row--after--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-data-list__item--m-ghost-row--after--Opacity: .6;\n --pf-c-data-list__item-control--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-data-list__item-control--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-data-list__item-control--MarginRight: var(--pf-global--spacer--md);\n --pf-c-data-list__item-control--md--MarginRight: var(--pf-global--spacer--xl);\n --pf-c-data-list__item-control--not-last-child--MarginRight: var(--pf-global--spacer--md);\n --pf-c-data-list__item-action--Display: flex;\n --pf-c-data-list__item-action--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-data-list__item-action--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-data-list__item-action--md--MarginLeft: var(--pf-global--spacer--xl);\n --pf-c-data-list__item-action--not-last-child--MarginRight: var(--pf-global--spacer--md);\n --pf-c-data-list__action--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-data-list__expandable-content--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-data-list__expandable-content--BorderTopColor: var(--pf-global--BorderColor--100);\n --pf-c-data-list__expandable-content--MarginRight: calc(var(--pf-c-data-list__expandable-content-body--PaddingRight) * -1);\n --pf-c-data-list__expandable-content--MarginLeft: calc(var(--pf-c-data-list__expandable-content-body--PaddingLeft) * -1);\n --pf-c-data-list__expandable-content--MaxHeight: 37.5rem;\n --pf-c-data-list__expandable-content--before--Top: calc(var(--pf-c-data-list__item--BorderBottomWidth) * -1);\n --pf-c-data-list__expandable-content-body--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-data-list__expandable-content-body--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-data-list__expandable-content-body--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-data-list__expandable-content-body--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-data-list__expandable-content-body--md--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-data-list__expandable-content-body--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-data-list__expandable-content-body--md--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-data-list__expandable-content-body--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-data-list--m-compact--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-data-list--m-compact__check--FontSize: var(--pf-global--FontSize--md);\n --pf-c-data-list--m-compact__cell--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-data-list--m-compact__cell--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-data-list--m-compact__cell--md--PaddingBottom: 0;\n --pf-c-data-list--m-compact__cell-cell--PaddingTop: 0;\n --pf-c-data-list--m-compact__cell-cell--md--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-data-list--m-compact__cell--cell--MarginRight: var(--pf-global--spacer--md);\n --pf-c-data-list--m-compact__item-control--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-data-list--m-compact__item-control--PaddingBottom: 0;\n --pf-c-data-list--m-compact__item-control--MarginRight: var(--pf-global--spacer--md);\n --pf-c-data-list--m-compact__item-action--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-data-list--m-compact__item-action--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-data-list--m-compact__item-action--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-data-list--m-compact__item-content--PaddingBottom: var(--pf-global--spacer--sm);\n color: var(--pf-global--Color--100);\n overflow-wrap: break-word;\n list-style-type: disc;\n border-top: var(--pf-c-data-list--BorderTopWidth) solid var(--pf-c-data-list--BorderTopColor); }\n @media screen and (min-width: 576px) {\n .pf-c-data-list {\n --pf-c-data-list--BorderTopColor: var(--pf-c-data-list--sm--BorderTopColor);\n --pf-c-data-list--BorderTopWidth: var(--pf-c-data-list--sm--BorderTopWidth); } }\n @media screen and (min-width: 576px) {\n .pf-c-data-list {\n --pf-c-data-list__item--BorderBottomWidth: var(--pf-c-data-list__item--sm--BorderBottomWidth);\n --pf-c-data-list__item--BorderBottomColor: var(--pf-c-data-list__item--sm--BorderBottomColor); } }\n @media (min-width: 576px) {\n .pf-c-data-list {\n --pf-c-data-list__item--before--Top: var(--pf-c-data-list__item--before--sm--Top); } }\n .pf-c-data-list.pf-m-compact {\n font-size: var(--pf-c-data-list--m-compact--FontSize);\n --pf-c-data-list__item-action--MarginLeft: var(--pf-c-data-list--m-compact__item-action--MarginLeft);\n --pf-c-data-list__item-action--PaddingTop: var(--pf-c-data-list--m-compact__item-action--PaddingTop);\n --pf-c-data-list__item-action--PaddingBottom: var(--pf-c-data-list--m-compact__item-action--PaddingBottom);\n --pf-c-data-list__item-control--MarginRight: var(--pf-c-data-list--m-compact__item-control--MarginRight);\n --pf-c-data-list__item-control--PaddingTop: var(--pf-c-data-list--m-compact__item-control--PaddingTop);\n --pf-c-data-list__item-control--PaddingBottom: var(--pf-c-data-list--m-compact__item-control--PaddingBottom);\n --pf-c-data-list__item-content--md--PaddingBottom: var(--pf-c-data-list--m-compact__item-content--PaddingBottom); }\n .pf-c-data-list.pf-m-compact .pf-c-data-list__cell {\n --pf-c-data-list__cell--PaddingTop: var(--pf-c-data-list--m-compact__cell--PaddingTop);\n --pf-c-data-list__cell--PaddingBottom: var(--pf-c-data-list--m-compact__cell--PaddingBottom);\n --pf-c-data-list__cell--MarginRight: var(--pf-c-data-list--m-compact__cell--cell--MarginRight);\n --pf-c-data-list__cell--cell--PaddingTop: var(--pf-c-data-list--m-compact__cell-cell--PaddingTop); }\n .pf-c-data-list.pf-m-compact .pf-c-data-list__check {\n font-size: var(--pf-c-data-list--m-compact__check--FontSize); }\n .pf-c-data-list.pf-m-drag-over {\n overflow-anchor: none; }\n\n.pf-c-data-list.pf-m-truncate,\n.pf-c-data-list__item-row.pf-m-truncate,\n.pf-c-data-list__cell.pf-m-truncate,\n.pf-c-data-list__text.pf-m-truncate {\n --pf-c-data-list--cell--MinWidth: var(--pf-c-data-list--cell--m-truncate--MinWidth);\n --pf-c-data-list--cell--Overflow: hidden;\n --pf-c-data-list--cell--TextOverflow: ellipsis;\n --pf-c-data-list--cell--WhiteSpace: nowrap; }\n\n.pf-c-data-list.pf-m-break-word,\n.pf-c-data-list__item-row.pf-m-break-word,\n.pf-c-data-list__cell.pf-m-break-word,\n.pf-c-data-list__text.pf-m-break-word {\n --pf-c-data-list--cell--WordBreak: break-word; }\n\n.pf-c-data-list.pf-m-nowrap,\n.pf-c-data-list__item-row.pf-m-nowrap,\n.pf-c-data-list__cell.pf-m-nowrap,\n.pf-c-data-list__text.pf-m-nowrap {\n --pf-c-data-list--cell--WhiteSpace: nowrap; }\n\n.pf-c-data-list__item {\n position: relative;\n display: flex;\n flex-direction: column;\n background-color: var(--pf-c-data-list__item--BackgroundColor);\n border-bottom: var(--pf-c-data-list__item--BorderBottomWidth) solid var(--pf-c-data-list__item--BorderBottomColor); }\n .pf-c-data-list__item::before {\n position: absolute;\n top: var(--pf-c-data-list__item--before--Top);\n bottom: 0;\n left: 0;\n width: var(--pf-c-data-list__item--before--Width);\n content: \"\";\n background-color: var(--pf-c-data-list__item--before--BackgroundColor);\n transition: var(--pf-c-data-list__item--before--Transition); }\n .pf-c-data-list__item.pf-m-selectable {\n cursor: pointer;\n outline-offset: var(--pf-c-data-list__item--m-selectable--OutlineOffset); }\n .pf-c-data-list__item.pf-m-selectable:hover, .pf-c-data-list__item.pf-m-selectable:focus {\n position: relative;\n z-index: var(--pf-c-data-list__item--m-selectable--hover--ZIndex); }\n .pf-c-data-list__item.pf-m-selectable:hover:not(.pf-m-selected):not(:last-child), .pf-c-data-list__item.pf-m-selectable:focus:not(.pf-m-selected):not(:last-child) {\n --pf-c-data-list__item--BorderBottomWidth: 0; }\n .pf-c-data-list__item.pf-m-selectable:hover:not(.pf-m-selected):not(:last-child) + .pf-c-data-list__item, .pf-c-data-list__item.pf-m-selectable:focus:not(.pf-m-selected):not(:last-child) + .pf-c-data-list__item {\n border-top: var(--pf-c-data-list__item--m-selectable--hover--item--BorderTopWidth) solid var(--pf-c-data-list__item--m-selectable--hover--item--BorderTopColor); }\n .pf-c-data-list__item.pf-m-selectable:hover {\n box-shadow: var(--pf-c-data-list__item--m-selectable--hover--BoxShadow); }\n .pf-c-data-list__item.pf-m-selectable:focus {\n box-shadow: var(--pf-c-data-list__item--m-selectable--focus--BoxShadow); }\n .pf-c-data-list__item.pf-m-selectable:active {\n box-shadow: var(--pf-c-data-list__item--m-selectable--active--BoxShadow); }\n .pf-c-data-list__item.pf-m-selected {\n --pf-c-data-list__item--before--BackgroundColor: var(--pf-c-data-list__item--m-selected--before--BackgroundColor);\n position: relative;\n z-index: var(--pf-c-data-list__item--m-selected--ZIndex);\n box-shadow: var(--pf-c-data-list__item--m-selected--BoxShadow); }\n .pf-c-data-list__item.pf-m-ghost-row::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n background-color: var(--pf-c-data-list__item--m-ghost-row--after--BackgroundColor);\n opacity: var(--pf-c-data-list__item--m-ghost-row--after--Opacity); }\n .pf-c-data-list__item.pf-m-expanded {\n --pf-c-data-list__item--before--BackgroundColor: var(--pf-c-data-list__item--m-expanded--before--BackgroundColor); }\n .pf-c-data-list__item.pf-m-expanded.pf-m-selectable:not(.pf-m-selected) {\n --pf-c-data-list__item--before--BackgroundColor: var(--pf-c-data-list__item--m-expanded--m-selectable--before--BackgroundColor); }\n\n.pf-c-data-list__item-row {\n display: flex;\n flex-wrap: nowrap;\n padding-right: var(--pf-c-data-list__item-row--PaddingRight);\n padding-left: var(--pf-c-data-list__item-row--PaddingLeft); }\n\n.pf-c-data-list__item-control {\n display: flex;\n flex-wrap: nowrap;\n padding-top: var(--pf-c-data-list__item-control--PaddingTop);\n padding-bottom: var(--pf-c-data-list__item-control--PaddingBottom);\n margin-right: var(--pf-c-data-list__item-control--MarginRight); }\n .pf-c-data-list__item-control > *:not(:last-child) {\n margin-right: var(--pf-c-data-list__item-control--not-last-child--MarginRight); }\n\n.pf-c-data-list__item-draggable-button {\n padding-right: var(--pf-c-data-list__item-draggable-button--PaddingRight);\n padding-left: var(--pf-c-data-list__item-draggable-button--PaddingLeft);\n margin-top: var(--pf-c-data-list__item-draggable-button--MarginTop);\n margin-left: var(--pf-c-data-list__item-draggable-button--MarginLeft);\n border: 0; }\n .pf-c-data-list__item-draggable-button:hover {\n --pf-c-data-list__item-draggable-button-icon--Color: var(--pf-c-data-list__item-draggable-button--hover__draggable-icon--Color);\n cursor: grab; }\n .pf-c-data-list__item-draggable-button:focus {\n --pf-c-data-list__item-draggable-button-icon--Color: var(--pf-c-data-list__item-draggable-button--focus__draggable-icon--Color); }\n .pf-c-data-list__item-draggable-button:active {\n cursor: grabbing; }\n .pf-c-data-list__item-draggable-button.pf-m-disabled {\n --pf-c-data-list__item-draggable-button-icon--Color: var(--pf-c-data-list__item-draggable-button--m-disabled__draggable-icon--Color);\n pointer-events: none;\n cursor: none; }\n .pf-c-data-list__item-draggable-button .pf-c-data-list__item-draggable-icon {\n color: var(--pf-c-data-list__item-draggable-button-icon--Color); }\n\n.pf-c-data-list__item-action {\n --pf-hidden-visible--visible--Display: var(--pf-c-data-list__item-action--Display);\n align-items: flex-start;\n align-content: flex-start;\n padding-top: var(--pf-c-data-list__item-action--PaddingTop);\n padding-bottom: var(--pf-c-data-list__item-action--PaddingBottom);\n margin-left: var(--pf-c-data-list__item-action--MarginLeft); }\n .pf-c-data-list__item-action > *:not(:last-child) {\n margin-right: var(--pf-c-data-list__item-action--not-last-child--MarginRight); }\n .pf-c-data-list__item-action .pf-c-data-list__action {\n margin-top: var(--pf-c-data-list__action--MarginTop); }\n\n.pf-c-data-list__toggle {\n margin-top: var(--pf-c-data-list__toggle--MarginTop);\n margin-left: var(--pf-c-data-list__toggle--MarginLeft); }\n\n.pf-c-data-list__toggle-icon {\n pointer-events: none;\n transition: var(--pf-c-data-list__toggle-icon--Transition); }\n .pf-c-data-list__item.pf-m-expanded .pf-c-data-list__toggle-icon {\n transform: rotate(var(--pf-c-data-list__item--m-expanded__toggle-icon--Rotate)); }\n\n.pf-c-data-list__item-content {\n display: grid;\n width: 100%;\n grid-template-columns: auto 1fr; }\n\n.pf-c-data-list__cell {\n flex: 1;\n grid-column: 1 / -1;\n padding-top: var(--pf-c-data-list__cell--PaddingTop);\n padding-bottom: var(--pf-c-data-list__cell--PaddingBottom); }\n .pf-c-data-list__cell + .pf-c-data-list__cell {\n flex: 1 0 100%;\n order: 1;\n padding-top: var(--pf-c-data-list__cell--cell--PaddingTop); }\n .pf-c-data-list__cell.pf-m-icon {\n flex-grow: 0;\n margin-right: var(--pf-c-data-list__cell--m-icon--MarginRight);\n grid-column: 1 / 2; }\n .pf-c-data-list__cell.pf-m-icon + .pf-c-data-list__cell {\n grid-column: 2 / 3;\n padding-top: var(--pf-c-data-list__cell--m-icon--cell--PaddingTop); }\n .pf-c-data-list__cell.pf-m-align-right {\n margin-left: 0; }\n\n.pf-c-data-list__text {\n display: inline-block; }\n\n.pf-c-data-list__text,\n.pf-c-data-list__cell {\n min-width: var(--pf-c-data-list--cell--MinWidth);\n max-width: 100%;\n overflow: var(--pf-c-data-list--cell--Overflow);\n text-overflow: var(--pf-c-data-list--cell--TextOverflow);\n word-break: var(--pf-c-data-list--cell--WordBreak);\n white-space: var(--pf-c-data-list--cell--WhiteSpace); }\n\n.pf-c-data-list__expandable-content {\n max-height: var(--pf-c-data-list__expandable-content--MaxHeight);\n overflow-y: auto;\n border-top: var(--pf-c-data-list__expandable-content--BorderTopWidth) solid var(--pf-c-data-list__expandable-content--BorderTopColor); }\n .pf-c-data-list__expandable-content .pf-c-data-list__expandable-content-body {\n padding: var(--pf-c-data-list__expandable-content-body--PaddingTop) var(--pf-c-data-list__expandable-content-body--PaddingRight) var(--pf-c-data-list__expandable-content-body--PaddingBottom) var(--pf-c-data-list__expandable-content-body--PaddingLeft); }\n .pf-c-data-list__expandable-content .pf-c-data-list__expandable-content-body.pf-m-no-padding {\n padding: 0; }\n\n.pf-c-description-list {\n --pf-c-description-list--RowGap: var(--pf-global--gutter--md);\n --pf-c-description-list--ColumnGap: var(--pf-global--spacer--lg);\n --pf-c-description-list--GridTemplateColumns--count: 1;\n --pf-c-description-list--GridTemplateColumns--width: 1fr;\n --pf-c-description-list--GridTemplateColumns: repeat(var(--pf-c-description-list--GridTemplateColumns--count), var(--pf-c-description-list--GridTemplateColumns--width));\n --pf-c-description-list__group--RowGap: var(--pf-global--spacer--sm);\n --pf-c-description-list__group--ColumnGap: var(--pf-global--spacer--md);\n --pf-c-description-list__group--GridTemplateColumns: auto;\n --pf-c-description-list__group--GridColumn: auto;\n --pf-c-description-list__term--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-description-list__term--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-description-list--m-horizontal__term--width: 12ch;\n --pf-c-description-list--m-horizontal__description--width: minmax(10ch, auto);\n --pf-c-description-list--m-horizontal__group--GridTemplateColumns: var(--pf-c-description-list__term--width) var(--pf-c-description-list--m-horizontal__description--width);\n --pf-c-description-list--m-1-col--GridTemplateColumns--count: 1;\n --pf-c-description-list--m-auto-fit--GridTemplateColumns--min: 15.625rem;\n --pf-c-description-list--m-auto-fit--GridTemplateColumns--minmax--min: var(--pf-c-description-list--m-auto-fit--GridTemplateColumns--min);\n display: grid;\n align-items: baseline;\n row-gap: var(--pf-c-description-list--RowGap);\n column-gap: var(--pf-c-description-list--ColumnGap);\n grid-template-columns: var(--pf-c-description-list--GridTemplateColumns); }\n @media screen and (min-width: 768px) {\n .pf-c-description-list {\n --pf-c-description-list--m-2-col--GridTemplateColumns--count: 2;\n --pf-c-description-list--m-3-col--GridTemplateColumns--count: 3; } }\n .pf-c-description-list.pf-m-horizontal {\n --pf-c-description-list__group--GridTemplateColumns: var(--pf-c-description-list--m-horizontal__group--GridTemplateColumns);\n --pf-c-description-list__term--width: var(--pf-c-description-list--m-horizontal__term--width); }\n @media (min-width: 768px) {\n .pf-c-description-list.pf-m-horizontal {\n --pf-c-description-list__term--width: var(--pf-c-description-list--m-horizontal__term--width-on-md, var(--pf-c-description-list--m-horizontal__term--width)); } }\n @media (min-width: 992px) {\n .pf-c-description-list.pf-m-horizontal {\n --pf-c-description-list__term--width: var(--pf-c-description-list--m-horizontal__term--width-on-lg, var(--pf-c-description-list--m-horizontal__term--width-on-md, var(--pf-c-description-list--m-horizontal__term--width))); } }\n @media (min-width: 1200px) {\n .pf-c-description-list.pf-m-horizontal {\n --pf-c-description-list__term--width: var(--pf-c-description-list--m-horizontal__term--width-on-xl, var(--pf-c-description-list--m-horizontal__term--width-on-lg, var(--pf-c-description-list--m-horizontal__term--width-on-md, var(--pf-c-description-list--m-horizontal__term--width)))); } }\n @media (min-width: 1450px) {\n .pf-c-description-list.pf-m-horizontal {\n --pf-c-description-list__term--width: var(--pf-c-description-list--m-horizontal__term--width-on-2xl, var(--pf-c-description-list--m-horizontal__term--width-on-xl, var(--pf-c-description-list--m-horizontal__term--width-on-lg, var(--pf-c-description-list--m-horizontal__term--width-on-md, var(--pf-c-description-list--m-horizontal__term--width))))); } }\n .pf-c-description-list.pf-m-inline-grid {\n display: inline-grid; }\n .pf-c-description-list.pf-m-auto-column-widths {\n --pf-c-description-list--GridTemplateColumns--width: minmax(8ch, max-content); }\n .pf-c-description-list.pf-m-auto-fit {\n grid-template-columns: repeat(auto-fit, minmax(var(--pf-c-description-list--m-auto-fit--GridTemplateColumns--minmax--min), 1fr));\n --pf-c-description-list--GridTemplateColumns--minmax--min: var(--pf-c-description-list--GridTemplateColumns--min); }\n @media (min-width: 768px) {\n .pf-c-description-list.pf-m-auto-fit {\n --pf-c-description-list--GridTemplateColumns--minmax--min: var(--pf-c-description-list--GridTemplateColumns--min-on-md, var(--pf-c-description-list--GridTemplateColumns--min)); } }\n @media (min-width: 992px) {\n .pf-c-description-list.pf-m-auto-fit {\n --pf-c-description-list--GridTemplateColumns--minmax--min: var(--pf-c-description-list--GridTemplateColumns--min-on-lg, var(--pf-c-description-list--GridTemplateColumns--min-on-md, var(--pf-c-description-list--GridTemplateColumns--min))); } }\n @media (min-width: 1200px) {\n .pf-c-description-list.pf-m-auto-fit {\n --pf-c-description-list--GridTemplateColumns--minmax--min: var(--pf-c-description-list--GridTemplateColumns--min-on-xl, var(--pf-c-description-list--GridTemplateColumns--min-on-lg, var(--pf-c-description-list--GridTemplateColumns--min-on-md, var(--pf-c-description-list--GridTemplateColumns--min)))); } }\n @media (min-width: 1450px) {\n .pf-c-description-list.pf-m-auto-fit {\n --pf-c-description-list--GridTemplateColumns--minmax--min: var(--pf-c-description-list--GridTemplateColumns--min-on-2xl, var(--pf-c-description-list--GridTemplateColumns--min-on-xl, var(--pf-c-description-list--GridTemplateColumns--min-on-lg, var(--pf-c-description-list--GridTemplateColumns--min-on-md, var(--pf-c-description-list--GridTemplateColumns--min))))); } }\n\n.pf-c-description-list__group {\n display: grid;\n grid-column: var(--pf-c-description-list__group--GridColumn);\n row-gap: var(--pf-c-description-list__group--RowGap);\n column-gap: var(--pf-c-description-list__group--ColumnGap);\n grid-template-columns: var(--pf-c-description-list__group--GridTemplateColumns);\n align-items: baseline; }\n\n.pf-c-description-list__term,\n.pf-c-description-list__description {\n text-align: left; }\n\n.pf-c-description-list__term {\n font-size: var(--pf-c-description-list__term--FontSize);\n font-weight: var(--pf-c-description-list__term--FontWeight); }\n .pf-c-description-list__term .pf-c-description-list__text {\n display: inline; }\n\n.pf-c-description-list.pf-m-1-col {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-1-col--GridTemplateColumns--count); }\n\n.pf-c-description-list.pf-m-2-col {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-2-col--GridTemplateColumns--count); }\n\n.pf-c-description-list.pf-m-3-col {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-3-col--GridTemplateColumns--count); }\n\n@media (min-width: 768px) {\n .pf-c-description-list.pf-m-1-col-on-md {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-1-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-2-col-on-md {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-2-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-3-col-on-md {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-3-col--GridTemplateColumns--count); } }\n\n@media (min-width: 992px) {\n .pf-c-description-list.pf-m-1-col-on-lg {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-1-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-2-col-on-lg {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-2-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-3-col-on-lg {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-3-col--GridTemplateColumns--count); } }\n\n@media (min-width: 1200px) {\n .pf-c-description-list.pf-m-1-col-on-xl {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-1-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-2-col-on-xl {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-2-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-3-col-on-xl {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-3-col--GridTemplateColumns--count); } }\n\n@media (min-width: 1450px) {\n .pf-c-description-list.pf-m-1-col-on-2xl {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-1-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-2-col-on-2xl {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-2-col--GridTemplateColumns--count); }\n .pf-c-description-list.pf-m-3-col-on-2xl {\n --pf-c-description-list--GridTemplateColumns--count: var(--pf-c-description-list--m-3-col--GridTemplateColumns--count); } }\n\n.pf-c-dual-list-selector {\n --pf-c-dual-list-selector__header--GridArea: pane-header;\n --pf-c-dual-list-selector__tools--GridArea: pane-tools;\n --pf-c-dual-list-selector__status--GridArea: pane-status;\n --pf-c-dual-list-selector__menu--GridArea: pane-menu;\n --pf-c-dual-list-selector__controls--GridArea: controls;\n --pf-c-dual-list-selector--m-chosen__header--GridArea: pane-header-c;\n --pf-c-dual-list-selector--m-chosen__tools--GridArea: pane-tools-c;\n --pf-c-dual-list-selector--m-chosen__status--GridArea: pane-status-c;\n --pf-c-dual-list-selector--m-chosen__menu--GridArea: pane-menu-c;\n --pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--min: 12.5rem;\n --pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max: 28.125rem;\n --pf-c-dual-list-selector__header--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__title-text--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-dual-list-selector__tools--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-dual-list-selector__tools-filter--tools-actions--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__menu--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-dual-list-selector__menu--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-dual-list-selector__menu--MinHeight: 12.5rem;\n --pf-c-dual-list-selector__menu--MaxHeight: 20rem;\n --pf-c-dual-list-selector__item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-dual-list-selector__item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-dual-list-selector__item--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-dual-list-selector__item--BackgroundColor: transparent;\n --pf-c-dual-list-selector__item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-dual-list-selector__item--focus-within--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-dual-list-selector__item--m-selected--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-dual-list-selector__item--m-expandable--PaddingLeft: 0;\n --pf-c-dual-list-selector__item--indent--base: calc(var(--pf-global--spacer--md) + var(--pf-global--spacer--sm) + var(--pf-c-dual-list-selector__item--FontSize));\n --pf-c-dual-list-selector__item--nested-indent--base: calc(var(--pf-c-dual-list-selector__item--indent--base) - var(--pf-global--spacer--md));\n --pf-c-dual-list-selector__item-text--Color: var(--pf-global--Color--100);\n --pf-c-dual-list-selector__item--m-selected__text--Color: var(--pf-global--active-color--100);\n --pf-c-dual-list-selector__item--m-selected__text--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-dual-list-selector__status--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__status-text--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-dual-list-selector__status-text--Color: var(--pf-global--Color--200);\n --pf-c-dual-list-selector__controls--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-dual-list-selector__controls--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-dual-list-selector__item-toggle--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item-toggle--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item-toggle--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item-toggle--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-dual-list-selector__item-toggle--MarginTop: calc(var(--pf-global--spacer--sm) * -1);\n --pf-c-dual-list-selector__item-toggle--MarginBottom: calc(var(--pf-global--spacer--sm) * -1);\n --pf-c-dual-list-selector__list__list__item-toggle--Left: 0;\n --pf-c-dual-list-selector__list__list__item-toggle--TranslateX: -100%;\n --pf-c-dual-list-selector__item-check--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item-count--Marginleft: var(--pf-global--spacer--sm);\n --pf-c-dual-list-selector__item--c-badge--m-read--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-dual-list-selector__item-toggle-icon--Rotate: 0;\n --pf-c-dual-list-selector__list-item--m-expanded__item-toggle-icon--Rotate: 90deg;\n --pf-c-dual-list-selector__item-toggle-icon--Transition: var(--pf-global--Transition);\n --pf-c-dual-list-selector__item-toggle-icon--MinWidth: var(--pf-c-dual-list-selector__item--FontSize);\n display: grid;\n grid-template-areas: \"pane-header . pane-header-c\" \"pane-tools . pane-tools-c\" \"pane-status . pane-status-c\" \"pane-menu controls pane-menu-c\";\n grid-template-columns: minmax(var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--min), var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max)) min-content minmax(var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--min), var(--pf-c-dual-list-selector--GridTemplateColumns--pane--MinMax--max));\n grid-template-rows: repeat(3, auto) auto; }\n\n.pf-c-dual-list-selector__pane {\n display: contents; }\n .pf-c-dual-list-selector__pane.pf-m-chosen {\n --pf-c-dual-list-selector__header--GridArea: var(--pf-c-dual-list-selector--m-chosen__header--GridArea);\n --pf-c-dual-list-selector__tools--GridArea: var(--pf-c-dual-list-selector--m-chosen__tools--GridArea);\n --pf-c-dual-list-selector__status--GridArea: var(--pf-c-dual-list-selector--m-chosen__status--GridArea);\n --pf-c-dual-list-selector__menu--GridArea: var(--pf-c-dual-list-selector--m-chosen__menu--GridArea); }\n\n.pf-c-dual-list-selector__header {\n grid-area: var(--pf-c-dual-list-selector__header--GridArea);\n margin-bottom: var(--pf-c-dual-list-selector__header--MarginBottom); }\n\n.pf-c-dual-list-selector__title-text {\n font-weight: var(--pf-c-dual-list-selector__title-text--FontWeight); }\n\n.pf-c-dual-list-selector__tools {\n display: flex;\n grid-area: var(--pf-c-dual-list-selector__tools--GridArea);\n margin-bottom: var(--pf-c-dual-list-selector__tools--MarginBottom); }\n\n.pf-c-dual-list-selector__tools-filter {\n flex-grow: 1; }\n\n.pf-c-dual-list-selector__tools-actions {\n display: flex; }\n .pf-c-dual-list-selector__tools-filter ~ .pf-c-dual-list-selector__tools-actions {\n margin-left: var(--pf-c-dual-list-selector__tools-filter--tools-actions--MarginLeft); }\n\n.pf-c-dual-list-selector__status {\n display: flex;\n grid-area: var(--pf-c-dual-list-selector__status--GridArea);\n margin-bottom: var(--pf-c-dual-list-selector__status--MarginBottom); }\n\n.pf-c-dual-list-selector__status-text {\n flex-grow: 1;\n font-size: var(--pf-c-dual-list-selector__status-text--FontSize);\n color: var(--pf-c-dual-list-selector__status-text--Color); }\n\n.pf-c-dual-list-selector__menu {\n grid-area: var(--pf-c-dual-list-selector__menu--GridArea);\n min-height: var(--pf-c-dual-list-selector__menu--MinHeight);\n max-height: var(--pf-c-dual-list-selector__menu--MaxHeight);\n overflow: auto;\n border: var(--pf-c-dual-list-selector__menu--BorderWidth) solid var(--pf-c-dual-list-selector__menu--BorderColor); }\n\n.pf-c-dual-list-selector__list {\n display: flex;\n flex-direction: column; }\n .pf-c-dual-list-selector__list .pf-c-dual-list-selector__list {\n --pf-c-dual-list-selector__item-toggle--MarginTop: 0;\n --pf-c-dual-list-selector__item-toggle--MarginBottom: 0; }\n .pf-c-dual-list-selector__list .pf-c-dual-list-selector__list .pf-c-dual-list-selector__item-toggle {\n position: absolute;\n top: 0;\n left: var(--pf-c-dual-list-selector__list__list__item-toggle--Left);\n transform: translateX(var(--pf-c-dual-list-selector__list__list__item-toggle--TranslateX)); }\n\n.pf-c-dual-list-selector__list-item.pf-m-expandable {\n --pf-c-dual-list-selector__item--PaddingLeft: var(--pf-c-dual-list-selector__item--m-expandable--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item.pf-m-expanded > .pf-c-dual-list-selector__item {\n --pf-c-dual-list-selector__item-toggle-icon--Rotate: var(--pf-c-dual-list-selector__list-item--m-expanded__item-toggle-icon--Rotate); }\n\n.pf-c-dual-list-selector__item,\n.pf-c-dual-list-selector__main {\n display: flex; }\n\n.pf-c-dual-list-selector__item,\n.pf-c-dual-list-selector__item-main {\n flex-basis: 100%; }\n\n.pf-c-dual-list-selector__item {\n position: relative;\n width: 100%;\n padding: var(--pf-c-dual-list-selector__item--PaddingTop) var(--pf-c-dual-list-selector__item--PaddingRight) var(--pf-c-dual-list-selector__item--PaddingBottom) var(--pf-c-dual-list-selector__item--PaddingLeft);\n font-size: var(--pf-c-dual-list-selector__item--FontSize);\n text-align: left;\n cursor: pointer;\n background-color: var(--pf-c-dual-list-selector__item--BackgroundColor);\n border: 0; }\n .pf-c-dual-list-selector__item:hover {\n --pf-c-dual-list-selector__item--BackgroundColor: var(--pf-c-dual-list-selector__item--hover--BackgroundColor); }\n .pf-c-dual-list-selector__item:focus-within {\n --pf-c-dual-list-selector__item--BackgroundColor: var(--pf-c-dual-list-selector__item--focus-within--BackgroundColor); }\n .pf-c-dual-list-selector__item.pf-m-selected {\n --pf-c-dual-list-selector__item--BackgroundColor: var(--pf-c-dual-list-selector__item--m-selected--BackgroundColor); }\n .pf-c-dual-list-selector__item.pf-m-selected .pf-c-dual-list-selector__item-text {\n --pf-c-dual-list-selector__item-text--Color: var(--pf-c-dual-list-selector__item--m-selected__text--Color);\n font-weight: var(--pf-c-dual-list-selector__item--m-selected__text--FontWeight); }\n .pf-c-dual-list-selector__item.pf-m-check {\n --pf-c-dual-list-selector__item--m-selected--BackgroundColor: transparent; }\n .pf-c-dual-list-selector__item .pf-c-dual-list-selector__item-count {\n margin-left: var(--pf-c-dual-list-selector__item-count--Marginleft); }\n .pf-c-dual-list-selector__item .pf-c-dual-list-selector__item-count .pf-c-badge.pf-m-read {\n --pf-c-badge--m-read--BackgroundColor: var(--pf-c-dual-list-selector__item--c-badge--m-read--BackgroundColor); }\n\n.pf-c-dual-list-selector__item-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex-grow: 1;\n color: var(--pf-c-dual-list-selector__item-text--Color); }\n\n.pf-c-dual-list-selector__controls {\n grid-area: var(--pf-c-dual-list-selector__controls--GridArea);\n align-self: center;\n padding-right: var(--pf-c-dual-list-selector__controls--PaddingRight);\n padding-left: var(--pf-c-dual-list-selector__controls--PaddingLeft); }\n\n.pf-c-dual-list-selector__item-main {\n display: flex;\n min-width: 0; }\n\n.pf-c-dual-list-selector__item-toggle {\n padding: var(--pf-c-dual-list-selector__item-toggle--PaddingTop) var(--pf-c-dual-list-selector__item-toggle--PaddingRight) var(--pf-c-dual-list-selector__item-toggle--PaddingBottom) var(--pf-c-dual-list-selector__item-toggle--PaddingLeft);\n margin-top: var(--pf-c-dual-list-selector__item-toggle--MarginTop);\n margin-bottom: var(--pf-c-dual-list-selector__item-toggle--MarginBottom); }\n\n.pf-c-dual-list-selector__item-check {\n display: flex;\n align-items: center;\n margin-right: var(--pf-c-dual-list-selector__item-check--MarginRight); }\n\n.pf-c-dual-list-selector__item-toggle-icon {\n display: inline-block;\n min-width: var(--pf-c-dual-list-selector__item-toggle-icon--MinWidth);\n text-align: center;\n transition: var(--pf-c-dual-list-selector__item-toggle-icon--Transition);\n transform: rotate(var(--pf-c-dual-list-selector__item-toggle-icon--Rotate)); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 1 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 2 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 3 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 4 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 5 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 6 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 7 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 8 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 9 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item .pf-c-dual-list-selector__list-item {\n --pf-c-dual-list-selector__item--PaddingLeft: calc(var(--pf-c-dual-list-selector__item--nested-indent--base) * 10 + var(--pf-c-dual-list-selector__item--indent--base));\n --pf-c-dual-list-selector__list__list__item-toggle--Left: var(--pf-c-dual-list-selector__item--PaddingLeft); }\n\n.pf-c-toolbar {\n --pf-c-toolbar--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-toolbar--RowGap: var(--pf-global--spacer--lg);\n --pf-c-toolbar--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-toolbar--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-toolbar--m-page-insets--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar--m-page-insets--xl--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__expandable-content--PaddingTop: var(--pf-c-toolbar--RowGap);\n --pf-c-toolbar__expandable-content--PaddingRight: var(--pf-c-toolbar__content--PaddingRight);\n --pf-c-toolbar__expandable-content--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-toolbar__expandable-content--PaddingLeft: var(--pf-c-toolbar__content--PaddingLeft);\n --pf-c-toolbar__expandable-content--lg--PaddingRight: 0;\n --pf-c-toolbar__expandable-content--lg--PaddingBottom: 0;\n --pf-c-toolbar__expandable-content--lg--PaddingLeft: 0;\n --pf-c-toolbar__expandable-content--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-toolbar__expandable-content--BoxShadow: var(--pf-global--BoxShadow--md-bottom);\n --pf-c-toolbar__expandable-content--BackgroundColor: var(--pf-c-toolbar--BackgroundColor);\n --pf-c-toolbar__expandable-content--m-expanded--GridRowGap: var(--pf-global--gutter--md);\n --pf-c-toolbar__group--m-chip-container--MarginTop: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-toolbar__group--m-chip-container__item--MarginTop: var(--pf-global--spacer--md);\n --pf-c-toolbar--spacer--base: var(--pf-global--spacer--md);\n --pf-c-toolbar__item--spacer: var(--pf-c-toolbar--spacer--base);\n --pf-c-toolbar__group--spacer: var(--pf-c-toolbar--spacer--base);\n --pf-c-toolbar__group--m-toggle-group--spacer: var(--pf-global--spacer--sm);\n --pf-c-toolbar__group--m-toggle-group--m-show--spacer: var(--pf-c-toolbar__group--spacer);\n --pf-c-toolbar__group--m-icon-button-group--spacer: var(--pf-c-toolbar__group--spacer);\n --pf-c-toolbar__group--m-icon-button-group--space-items: 0;\n --pf-c-toolbar__group--m-button-group--spacer: var(--pf-c-toolbar__group--spacer);\n --pf-c-toolbar__group--m-button-group--space-items: var(--pf-global--spacer--sm);\n --pf-c-toolbar__group--m-filter-group--spacer: var(--pf-c-toolbar__group--spacer);\n --pf-c-toolbar__group--m-filter-group--space-items: 0;\n --pf-c-toolbar__item--m-overflow-menu--spacer: var(--pf-c-toolbar__item--spacer);\n --pf-c-toolbar__item--m-bulk-select--spacer: var(--pf-global--spacer--lg);\n --pf-c-toolbar__expand-all-icon--Rotate: 0;\n --pf-c-toolbar__expand-all-icon--Transition: var(--pf-global--Transition);\n --pf-c-toolbar__item--m-expand-all--m-expanded__expand-all-icon--Rotate: 90deg;\n --pf-c-toolbar__item--m-search-filter--spacer: var(--pf-global--spacer--sm);\n --pf-c-toolbar__item--m-chip-group--spacer: var(--pf-global--spacer--sm);\n --pf-c-toolbar__item--m-label--spacer: var(--pf-c-toolbar__item--spacer);\n --pf-c-toolbar__item--m-label--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-toolbar__toggle--m-expanded__c-button--m-plain--Color: var(--pf-global--Color--100);\n --pf-c-toolbar--c-divider--m-vertical--spacer: var(--pf-c-toolbar--spacer--base);\n position: relative;\n row-gap: var(--pf-c-toolbar--RowGap);\n display: grid;\n padding-top: var(--pf-c-toolbar--PaddingTop);\n padding-bottom: var(--pf-c-toolbar--PaddingBottom);\n background-color: var(--pf-c-toolbar--BackgroundColor); }\n @media screen and (min-width: 992px) {\n .pf-c-toolbar {\n --pf-c-toolbar__expandable-content--PaddingRight: var(--pf-c-toolbar__expandable-content--lg--PaddingRight);\n --pf-c-toolbar__expandable-content--PaddingBottom: var(--pf-c-toolbar__expandable-content--lg--PaddingBottom);\n --pf-c-toolbar__expandable-content--PaddingLeft: var(--pf-c-toolbar__expandable-content--lg--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-toolbar {\n --pf-c-toolbar--m-page-insets--inset: var(--pf-c-toolbar--m-page-insets--xl--inset); } }\n .pf-c-toolbar.pf-m-page-insets {\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--m-page-insets--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--m-page-insets--inset); }\n\n.pf-c-toolbar__content-section > .pf-c-divider,\n.pf-c-toolbar__group > .pf-c-divider {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar--c-divider--m-vertical--spacer); }\n\n.pf-c-toolbar__content-section > .pf-c-divider.pf-m-vertical,\n.pf-c-toolbar__group > .pf-c-divider.pf-m-vertical {\n margin-right: var(--pf-c-toolbar--spacer); }\n .pf-c-toolbar__content-section > .pf-c-divider.pf-m-vertical:last-child,\n .pf-c-toolbar__group > .pf-c-divider.pf-m-vertical:last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar__group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--spacer);\n display: flex;\n align-items: center;\n margin-right: var(--pf-c-toolbar--spacer); }\n .pf-c-toolbar__group.pf-m-button-group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-button-group--spacer); }\n .pf-c-toolbar__group.pf-m-button-group > * {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-button-group--space-items); }\n .pf-c-toolbar__group.pf-m-icon-button-group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-icon-button-group--spacer); }\n .pf-c-toolbar__group.pf-m-icon-button-group > * {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-icon-button-group--space-items); }\n .pf-c-toolbar__group.pf-m-filter-group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-filter-group--spacer); }\n .pf-c-toolbar__group.pf-m-filter-group > * {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-filter-group--space-items); }\n .pf-c-toolbar__group.pf-m-filter-group > * + * {\n margin-left: -1px; }\n .pf-c-toolbar__group.pf-m-toggle-group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--spacer); }\n .pf-c-toolbar__group.pf-m-toggle-group .pf-c-toolbar__group,\n .pf-c-toolbar__group.pf-m-toggle-group .pf-c-toolbar__item {\n display: none;\n visibility: hidden; }\n .pf-c-toolbar__group.pf-m-toggle-group .pf-c-toolbar__toggle {\n display: inline-block;\n visibility: visible; }\n .pf-c-toolbar__group:last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar__item {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--spacer);\n margin-right: var(--pf-c-toolbar--spacer); }\n .pf-c-toolbar__item.pf-m-overflow-menu {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--m-overflow-menu--spacer); }\n .pf-c-toolbar__item.pf-m-bulk-select {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--m-bulk-select--spacer); }\n .pf-c-toolbar__item.pf-m-expand-all.pf-m-expanded {\n --pf-c-toolbar__expand-all-icon--Rotate: var(--pf-c-toolbar__item--m-expand-all--m-expanded__expand-all-icon--Rotate); }\n .pf-c-toolbar__item.pf-m-search-filter {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--m-search-filter--spacer); }\n .pf-c-toolbar__item.pf-m-chip-group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--m-chip-group--spacer); }\n .pf-c-toolbar__item.pf-m-label {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--m-label--spacer);\n font-weight: var(--pf-c-toolbar__item--m-label--FontWeight); }\n .pf-c-toolbar__item.pf-m-pagination {\n margin-left: auto; }\n .pf-c-toolbar__item.pf-m-pagination .pf-c-pagination {\n flex-wrap: nowrap; }\n .pf-c-toolbar__item:last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar__expand-all-icon {\n display: inline-block;\n transition: var(--pf-c-toolbar__expand-all-icon--Transition);\n transform: rotate(var(--pf-c-toolbar__expand-all-icon--Rotate)); }\n\n.pf-c-toolbar__content,\n.pf-c-toolbar__content-section {\n display: flex;\n flex-wrap: wrap;\n align-items: center; }\n\n.pf-c-toolbar__content {\n position: relative;\n padding-right: var(--pf-c-toolbar__content--PaddingRight);\n padding-left: var(--pf-c-toolbar__content--PaddingLeft); }\n\n.pf-c-toolbar__content-section {\n width: 100%; }\n\n.pf-c-toolbar__expandable-content {\n position: absolute;\n top: 100%;\n right: 0;\n left: 0;\n z-index: var(--pf-c-toolbar__expandable-content--ZIndex);\n display: none;\n width: 100%;\n padding: var(--pf-c-toolbar__expandable-content--PaddingTop) var(--pf-c-toolbar__expandable-content--PaddingRight) var(--pf-c-toolbar__expandable-content--PaddingBottom) var(--pf-c-toolbar__expandable-content--PaddingLeft);\n visibility: hidden;\n background-color: var(--pf-c-toolbar__expandable-content--BackgroundColor);\n box-shadow: var(--pf-c-toolbar__expandable-content--BoxShadow); }\n @media screen and (min-width: 992px) {\n .pf-c-toolbar__expandable-content {\n position: static;\n box-shadow: none; } }\n .pf-c-toolbar__expandable-content.pf-m-expanded {\n display: grid;\n grid-row-gap: var(--pf-c-toolbar__expandable-content--m-expanded--GridRowGap);\n visibility: visible; }\n .pf-c-toolbar__expandable-content .pf-c-toolbar__group,\n .pf-c-toolbar__expandable-content .pf-c-toolbar__item {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar__expandable-content .pf-c-toolbar__group {\n display: grid;\n grid-row-gap: var(--pf-c-toolbar__expandable-content--m-expanded--GridRowGap); }\n .pf-c-toolbar__expandable-content .pf-m-label {\n display: none;\n visibility: hidden; }\n\n.pf-c-toolbar__content.pf-m-chip-container,\n.pf-c-toolbar__group.pf-m-chip-container {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n margin-top: var(--pf-c-toolbar__group--m-chip-container--MarginTop);\n grid-row-gap: 0; }\n .pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__item,\n .pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__item {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__item--spacer);\n margin-top: var(--pf-c-toolbar__group--m-chip-container__item--MarginTop); }\n .pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__group,\n .pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__group {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--spacer);\n display: flex;\n flex-wrap: wrap;\n grid-row-gap: 0; }\n .pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__group:last-child,\n .pf-c-toolbar__content.pf-m-chip-container .pf-c-toolbar__item:last-child,\n .pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__group:last-child,\n .pf-c-toolbar__group.pf-m-chip-container .pf-c-toolbar__item:last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar .pf-c-chip-group:last-child {\n --pf-c-chip-group--MarginRight: 0; }\n\n.pf-c-toolbar .pf-c-chip-group li:last-child {\n --pf-c-chip-group__li--m-toolbar--MarginRight: 0; }\n\n.pf-c-toolbar__toggle.pf-m-expanded .pf-c-button.pf-m-plain {\n color: var(--pf-c-toolbar__toggle--m-expanded__c-button--m-plain--Color); }\n\n.pf-m-toggle-group.pf-m-show {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer); }\n .pf-m-toggle-group.pf-m-show .pf-c-toolbar__group,\n .pf-m-toggle-group.pf-m-show .pf-c-toolbar__item {\n display: flex;\n flex: 0 1 auto;\n visibility: visible; }\n .pf-m-toggle-group.pf-m-show .pf-c-toolbar__toggle {\n display: none;\n visibility: hidden; }\n\n@media (min-width: 576px) {\n .pf-m-toggle-group.pf-m-show-on-sm {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer); }\n .pf-m-toggle-group.pf-m-show-on-sm .pf-c-toolbar__group,\n .pf-m-toggle-group.pf-m-show-on-sm .pf-c-toolbar__item {\n display: flex;\n flex: 0 1 auto;\n visibility: visible; }\n .pf-m-toggle-group.pf-m-show-on-sm .pf-c-toolbar__toggle {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 768px) {\n .pf-m-toggle-group.pf-m-show-on-md {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer); }\n .pf-m-toggle-group.pf-m-show-on-md .pf-c-toolbar__group,\n .pf-m-toggle-group.pf-m-show-on-md .pf-c-toolbar__item {\n display: flex;\n flex: 0 1 auto;\n visibility: visible; }\n .pf-m-toggle-group.pf-m-show-on-md .pf-c-toolbar__toggle {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 992px) {\n .pf-m-toggle-group.pf-m-show-on-lg {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer); }\n .pf-m-toggle-group.pf-m-show-on-lg .pf-c-toolbar__group,\n .pf-m-toggle-group.pf-m-show-on-lg .pf-c-toolbar__item {\n display: flex;\n flex: 0 1 auto;\n visibility: visible; }\n .pf-m-toggle-group.pf-m-show-on-lg .pf-c-toolbar__toggle {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 1200px) {\n .pf-m-toggle-group.pf-m-show-on-xl {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer); }\n .pf-m-toggle-group.pf-m-show-on-xl .pf-c-toolbar__group,\n .pf-m-toggle-group.pf-m-show-on-xl .pf-c-toolbar__item {\n display: flex;\n flex: 0 1 auto;\n visibility: visible; }\n .pf-m-toggle-group.pf-m-show-on-xl .pf-c-toolbar__toggle {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 1450px) {\n .pf-m-toggle-group.pf-m-show-on-2xl {\n --pf-c-toolbar--spacer: var(--pf-c-toolbar__group--m-toggle-group--m-show--spacer); }\n .pf-m-toggle-group.pf-m-show-on-2xl .pf-c-toolbar__group,\n .pf-m-toggle-group.pf-m-show-on-2xl .pf-c-toolbar__item {\n display: flex;\n flex: 0 1 auto;\n visibility: visible; }\n .pf-m-toggle-group.pf-m-show-on-2xl .pf-c-toolbar__toggle {\n display: none;\n visibility: hidden; } }\n\n.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right,\n.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right {\n margin-left: auto; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right ~ .pf-m-pagination {\n margin-left: 0; }\n\n.pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left,\n.pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left ~ .pf-m-pagination {\n margin-left: auto; }\n\n.pf-c-toolbar .pf-m-hidden {\n display: none;\n visibility: hidden; }\n\n.pf-c-toolbar .pf-m-visible {\n display: flex;\n visibility: visible; }\n\n.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap,\n.pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap {\n flex-wrap: nowrap; }\n\n.pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap,\n.pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap {\n flex-wrap: wrap; }\n\n@media (min-width: 576px) {\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-sm,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-sm {\n margin-left: auto; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-sm ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-sm ~ .pf-m-pagination {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-sm,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-sm {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-sm ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-sm ~ .pf-m-pagination {\n margin-left: auto; }\n .pf-c-toolbar .pf-m-hidden-on-sm {\n display: none;\n visibility: hidden; }\n .pf-c-toolbar .pf-m-visible-on-sm {\n display: flex;\n visibility: visible; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-sm,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-sm {\n flex-wrap: nowrap; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-sm,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-sm {\n flex-wrap: wrap; } }\n\n@media (min-width: 768px) {\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-md,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-md {\n margin-left: auto; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-md ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-md ~ .pf-m-pagination {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-md,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-md {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-md ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-md ~ .pf-m-pagination {\n margin-left: auto; }\n .pf-c-toolbar .pf-m-hidden-on-md {\n display: none;\n visibility: hidden; }\n .pf-c-toolbar .pf-m-visible-on-md {\n display: flex;\n visibility: visible; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-md,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-md {\n flex-wrap: nowrap; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-md,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-md {\n flex-wrap: wrap; } }\n\n@media (min-width: 992px) {\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-lg,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-lg {\n margin-left: auto; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-lg ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-lg ~ .pf-m-pagination {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-lg,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-lg {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-lg ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-lg ~ .pf-m-pagination {\n margin-left: auto; }\n .pf-c-toolbar .pf-m-hidden-on-lg {\n display: none;\n visibility: hidden; }\n .pf-c-toolbar .pf-m-visible-on-lg {\n display: flex;\n visibility: visible; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-lg,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-lg {\n flex-wrap: nowrap; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-lg,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-lg {\n flex-wrap: wrap; } }\n\n@media (min-width: 1200px) {\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-xl {\n margin-left: auto; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-xl ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-xl ~ .pf-m-pagination {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-xl {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-xl ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-xl ~ .pf-m-pagination {\n margin-left: auto; }\n .pf-c-toolbar .pf-m-hidden-on-xl {\n display: none;\n visibility: hidden; }\n .pf-c-toolbar .pf-m-visible-on-xl {\n display: flex;\n visibility: visible; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-xl {\n flex-wrap: nowrap; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-xl {\n flex-wrap: wrap; } }\n\n@media (min-width: 1450px) {\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-2xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-2xl {\n margin-left: auto; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-right-on-2xl ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-right-on-2xl ~ .pf-m-pagination {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-2xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-2xl {\n margin-left: 0; }\n .pf-c-toolbar .pf-c-toolbar__item.pf-m-align-left-on-2xl ~ .pf-m-pagination,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-align-left-on-2xl ~ .pf-m-pagination {\n margin-left: auto; }\n .pf-c-toolbar .pf-m-hidden-on-2xl {\n display: none;\n visibility: hidden; }\n .pf-c-toolbar .pf-m-visible-on-2xl {\n display: flex;\n visibility: visible; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-nowrap-on-2xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-nowrap-on-2xl {\n flex-wrap: nowrap; }\n .pf-c-toolbar .pf-c-toolbar__content-section.pf-m-wrap-on-2xl,\n .pf-c-toolbar .pf-c-toolbar__group.pf-m-wrap-on-2xl {\n flex-wrap: wrap; } }\n\n.pf-c-toolbar .pf-m-space-items-none > * {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar .pf-m-space-items-none > :last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar .pf-m-space-items-sm > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n\n.pf-c-toolbar .pf-m-space-items-sm > :last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar .pf-m-space-items-md > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n\n.pf-c-toolbar .pf-m-space-items-md > :last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar .pf-m-space-items-lg > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n\n.pf-c-toolbar .pf-m-space-items-lg > :last-child {\n --pf-c-toolbar--spacer: 0; }\n\n@media (min-width: 576px) {\n .pf-c-toolbar .pf-m-space-items-none-on-sm > * {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-none-on-sm > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-sm-on-sm > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-space-items-sm-on-sm > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-md-on-sm > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-space-items-md-on-sm > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-lg-on-sm > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-space-items-lg-on-sm > :last-child {\n --pf-c-toolbar--spacer: 0; } }\n\n@media (min-width: 768px) {\n .pf-c-toolbar .pf-m-space-items-none-on-md > * {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-none-on-md > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-sm-on-md > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-space-items-sm-on-md > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-md-on-md > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-space-items-md-on-md > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-lg-on-md > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-space-items-lg-on-md > :last-child {\n --pf-c-toolbar--spacer: 0; } }\n\n@media (min-width: 992px) {\n .pf-c-toolbar .pf-m-space-items-none-on-lg > * {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-none-on-lg > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-sm-on-lg > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-space-items-sm-on-lg > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-md-on-lg > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-space-items-md-on-lg > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-lg-on-lg > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-space-items-lg-on-lg > :last-child {\n --pf-c-toolbar--spacer: 0; } }\n\n@media (min-width: 1200px) {\n .pf-c-toolbar .pf-m-space-items-none-on-xl > * {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-none-on-xl > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-sm-on-xl > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-space-items-sm-on-xl > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-md-on-xl > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-space-items-md-on-xl > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-lg-on-xl > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-space-items-lg-on-xl > :last-child {\n --pf-c-toolbar--spacer: 0; } }\n\n@media (min-width: 1450px) {\n .pf-c-toolbar .pf-m-space-items-none-on-2xl > * {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-none-on-2xl > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-sm-on-2xl > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-space-items-sm-on-2xl > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-md-on-2xl > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-space-items-md-on-2xl > :last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-space-items-lg-on-2xl > * {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-space-items-lg-on-2xl > :last-child {\n --pf-c-toolbar--spacer: 0; } }\n\n.pf-c-toolbar .pf-m-spacer-none {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-none:last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-toolbar .pf-m-spacer-sm {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-sm:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n\n.pf-c-toolbar .pf-m-spacer-md {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-md:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n\n.pf-c-toolbar .pf-m-spacer-lg {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-spacer-lg:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n\n@media (min-width: 576px) {\n .pf-c-toolbar .pf-m-spacer-none-on-sm {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-none-on-sm:last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-sm-on-sm {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-sm-on-sm:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-md-on-sm {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-md-on-sm:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-lg-on-sm {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-spacer-lg-on-sm:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); } }\n\n@media (min-width: 768px) {\n .pf-c-toolbar .pf-m-spacer-none-on-md {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-none-on-md:last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-sm-on-md {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-sm-on-md:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-md-on-md {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-md-on-md:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-lg-on-md {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-spacer-lg-on-md:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); } }\n\n@media (min-width: 992px) {\n .pf-c-toolbar .pf-m-spacer-none-on-lg {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-none-on-lg:last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-sm-on-lg {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-sm-on-lg:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-md-on-lg {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-md-on-lg:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-lg-on-lg {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-spacer-lg-on-lg:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); } }\n\n@media (min-width: 1200px) {\n .pf-c-toolbar .pf-m-spacer-none-on-xl {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-none-on-xl:last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-sm-on-xl {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-sm-on-xl:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-md-on-xl {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-md-on-xl:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-lg-on-xl {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-spacer-lg-on-xl:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); } }\n\n@media (min-width: 1450px) {\n .pf-c-toolbar .pf-m-spacer-none-on-2xl {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-none-on-2xl:last-child {\n --pf-c-toolbar--spacer: 0; }\n .pf-c-toolbar .pf-m-spacer-sm-on-2xl {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-sm-on-2xl:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--sm); }\n .pf-c-toolbar .pf-m-spacer-md-on-2xl {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-md-on-2xl:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--md); }\n .pf-c-toolbar .pf-m-spacer-lg-on-2xl {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); }\n .pf-c-toolbar .pf-m-spacer-lg-on-2xl:last-child {\n --pf-c-toolbar--spacer: var(--pf-global--spacer--lg); } }\n\n.pf-c-toolbar.pf-m-inset-none {\n --pf-c-toolbar--inset: 0;\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n\n.pf-c-toolbar.pf-m-inset-sm {\n --pf-c-toolbar--inset: var(--pf-global--spacer--sm);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n\n.pf-c-toolbar.pf-m-inset-md {\n --pf-c-toolbar--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n\n.pf-c-toolbar.pf-m-inset-lg {\n --pf-c-toolbar--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n\n.pf-c-toolbar.pf-m-inset-xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n\n.pf-c-toolbar.pf-m-inset-2xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--2xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n\n@media (min-width: 576px) {\n .pf-c-toolbar.pf-m-inset-none-on-sm {\n --pf-c-toolbar--inset: 0;\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-sm-on-sm {\n --pf-c-toolbar--inset: var(--pf-global--spacer--sm);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-md-on-sm {\n --pf-c-toolbar--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-lg-on-sm {\n --pf-c-toolbar--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-xl-on-sm {\n --pf-c-toolbar--inset: var(--pf-global--spacer--xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-2xl-on-sm {\n --pf-c-toolbar--inset: var(--pf-global--spacer--2xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); } }\n\n@media (min-width: 768px) {\n .pf-c-toolbar.pf-m-inset-none-on-md {\n --pf-c-toolbar--inset: 0;\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-sm-on-md {\n --pf-c-toolbar--inset: var(--pf-global--spacer--sm);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-md-on-md {\n --pf-c-toolbar--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-lg-on-md {\n --pf-c-toolbar--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-xl-on-md {\n --pf-c-toolbar--inset: var(--pf-global--spacer--xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-2xl-on-md {\n --pf-c-toolbar--inset: var(--pf-global--spacer--2xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); } }\n\n@media (min-width: 992px) {\n .pf-c-toolbar.pf-m-inset-none-on-lg {\n --pf-c-toolbar--inset: 0;\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-sm-on-lg {\n --pf-c-toolbar--inset: var(--pf-global--spacer--sm);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-md-on-lg {\n --pf-c-toolbar--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-lg-on-lg {\n --pf-c-toolbar--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-xl-on-lg {\n --pf-c-toolbar--inset: var(--pf-global--spacer--xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-2xl-on-lg {\n --pf-c-toolbar--inset: var(--pf-global--spacer--2xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); } }\n\n@media (min-width: 1200px) {\n .pf-c-toolbar.pf-m-inset-none-on-xl {\n --pf-c-toolbar--inset: 0;\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-sm-on-xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--sm);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-md-on-xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-lg-on-xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-xl-on-xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-2xl-on-xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--2xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); } }\n\n@media (min-width: 1450px) {\n .pf-c-toolbar.pf-m-inset-none-on-2xl {\n --pf-c-toolbar--inset: 0;\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-sm-on-2xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--sm);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-md-on-2xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--md);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-lg-on-2xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--lg);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-xl-on-2xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); }\n .pf-c-toolbar.pf-m-inset-2xl-on-2xl {\n --pf-c-toolbar--inset: var(--pf-global--spacer--2xl);\n --pf-c-toolbar__content--PaddingRight: var(--pf-c-toolbar--inset);\n --pf-c-toolbar__content--PaddingLeft: var(--pf-c-toolbar--inset); } }\n\n.pf-c-toolbar__content-section > :last-child {\n --pf-c-toolbar--spacer: 0; }\n\n.pf-c-date-picker {\n --pf-c-date-picker--m-top__calendar--Top: 0;\n --pf-c-date-picker--m-top__calendar--TranslateY: calc(-100% - var(--pf-global--spacer--xs));\n --pf-c-date-picker__helper-text--MarginTop: var(--pf-global--spacer--xs);\n --pf-c-date-picker__helper-text--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-date-picker__helper-text--Color: var(--pf-global--Color--100);\n --pf-c-date-picker__helper-text--m-error--Color: var(--pf-global--danger-color--100);\n --pf-c-date-picker__input--c-form-control--Width: calc(var(--pf-c-date-picker__input--c-form-control--width-chars) * 1ch + var(--pf-c-date-picker__input--c-form-control--width-base));\n --pf-c-date-picker__input--c-form-control--width-base: calc(var(--pf-global--spacer--xl) + var(--pf-global--spacer--sm));\n --pf-c-date-picker__input--c-form-control--width-chars: 10;\n --pf-c-date-picker__calendar--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-date-picker__calendar--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-date-picker__calendar--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-date-picker__calendar--Top: calc(100% + var(--pf-global--spacer--xs));\n --pf-c-date-picker__calendar--Right: auto;\n --pf-c-date-picker__calendar--Left: 0;\n --pf-c-date-picker__calendar--m-align-right--Right: 0;\n --pf-c-date-picker__calendar--m-align-right--Left: auto;\n position: relative;\n display: inline-block; }\n\n.pf-c-date-picker__helper-text {\n margin-top: var(--pf-c-date-picker__helper-text--MarginTop);\n font-size: var(--pf-c-date-picker__helper-text--FontSize);\n color: var(--pf-c-date-picker__helper-text--Color); }\n .pf-c-date-picker__helper-text.pf-m-error {\n --pf-c-date-picker__helper-text--Color: var(--pf-c-date-picker__helper-text--m-error--Color); }\n\n.pf-c-date-picker__input .pf-c-form-control {\n width: var(--pf-c-date-picker__input--c-form-control--Width); }\n\n.pf-c-date-picker__calendar {\n position: absolute;\n top: var(--pf-c-date-picker__calendar--Top);\n right: var(--pf-c-date-picker__calendar--Right);\n left: var(--pf-c-date-picker__calendar--Left);\n z-index: var(--pf-c-date-picker__calendar--ZIndex);\n background-color: var(--pf-c-date-picker__calendar--BackgroundColor);\n box-shadow: var(--pf-c-date-picker__calendar--BoxShadow); }\n .pf-c-date-picker__calendar.pf-m-align-right {\n --pf-c-date-picker__calendar--Right: var(--pf-c-date-picker__calendar--m-align-right--Right);\n --pf-c-date-picker__calendar--Left: var(--pf-c-date-picker__calendar--m-align-right--Left); }\n .pf-c-date-picker.pf-m-top .pf-c-date-picker__calendar {\n --pf-c-date-picker__calendar--Top: var(--pf-c-date-picker--m-top__calendar--Top);\n transform: translateY(var(--pf-c-date-picker--m-top__calendar--TranslateY)); }\n\n.pf-c-divider {\n --pf-c-divider--Height: var(--pf-global--BorderWidth--sm);\n --pf-c-divider--BackgroundColor: var(--pf-global--BorderColor--100);\n --pf-c-divider--after--Height: var(--pf-c-divider--Height);\n --pf-c-divider--after--BackgroundColor: var(--pf-c-divider--BackgroundColor);\n --pf-c-divider--after--FlexBasis: 100%;\n --pf-c-divider--after--Inset: 0%;\n --pf-c-divider--m-vertical--after--FlexBasis: 100%;\n --pf-c-divider--m-vertical--after--Width: var(--pf-global--BorderWidth--sm);\n display: flex;\n align-items: center;\n align-self: stretch;\n justify-content: center;\n width: 100%;\n border: 0; }\n .pf-c-divider::after {\n flex-basis: calc(var(--pf-c-divider--after--FlexBasis) - calc(var(--pf-c-divider--after--Inset) * 2));\n align-self: stretch;\n height: var(--pf-c-divider--after--Height);\n content: \"\";\n background-color: var(--pf-c-divider--after--BackgroundColor);\n justify-self: center; }\n .pf-c-divider.pf-m-vertical {\n display: inline-flex;\n flex-direction: column;\n width: auto;\n height: inherit;\n min-height: 100%;\n max-height: 100%; }\n .pf-c-divider.pf-m-vertical::after {\n flex-basis: calc(var(--pf-c-divider--m-vertical--after--FlexBasis) - var(--pf-c-divider--after--Inset));\n width: var(--pf-c-divider--m-vertical--after--Width); }\n .pf-c-divider.pf-m-inset-none {\n --pf-c-divider--after--Inset: 0%; }\n .pf-c-divider.pf-m-inset-xs {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xs); }\n .pf-c-divider.pf-m-inset-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--sm); }\n .pf-c-divider.pf-m-inset-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--md); }\n .pf-c-divider.pf-m-inset-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--lg); }\n .pf-c-divider.pf-m-inset-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xl); }\n .pf-c-divider.pf-m-inset-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--2xl); }\n .pf-c-divider.pf-m-inset-3xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--3xl); }\n @media (min-width: 576px) {\n .pf-c-divider.pf-m-inset-none-on-sm {\n --pf-c-divider--after--Inset: 0%; }\n .pf-c-divider.pf-m-inset-xs-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xs); }\n .pf-c-divider.pf-m-inset-sm-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--sm); }\n .pf-c-divider.pf-m-inset-md-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--md); }\n .pf-c-divider.pf-m-inset-lg-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--lg); }\n .pf-c-divider.pf-m-inset-xl-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xl); }\n .pf-c-divider.pf-m-inset-2xl-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--2xl); }\n .pf-c-divider.pf-m-inset-3xl-on-sm {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--3xl); } }\n @media (min-width: 768px) {\n .pf-c-divider.pf-m-inset-none-on-md {\n --pf-c-divider--after--Inset: 0%; }\n .pf-c-divider.pf-m-inset-xs-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xs); }\n .pf-c-divider.pf-m-inset-sm-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--sm); }\n .pf-c-divider.pf-m-inset-md-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--md); }\n .pf-c-divider.pf-m-inset-lg-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--lg); }\n .pf-c-divider.pf-m-inset-xl-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xl); }\n .pf-c-divider.pf-m-inset-2xl-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--2xl); }\n .pf-c-divider.pf-m-inset-3xl-on-md {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--3xl); } }\n @media (min-width: 992px) {\n .pf-c-divider.pf-m-inset-none-on-lg {\n --pf-c-divider--after--Inset: 0%; }\n .pf-c-divider.pf-m-inset-xs-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xs); }\n .pf-c-divider.pf-m-inset-sm-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--sm); }\n .pf-c-divider.pf-m-inset-md-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--md); }\n .pf-c-divider.pf-m-inset-lg-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--lg); }\n .pf-c-divider.pf-m-inset-xl-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xl); }\n .pf-c-divider.pf-m-inset-2xl-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--2xl); }\n .pf-c-divider.pf-m-inset-3xl-on-lg {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--3xl); } }\n @media (min-width: 1200px) {\n .pf-c-divider.pf-m-inset-none-on-xl {\n --pf-c-divider--after--Inset: 0%; }\n .pf-c-divider.pf-m-inset-xs-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xs); }\n .pf-c-divider.pf-m-inset-sm-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--sm); }\n .pf-c-divider.pf-m-inset-md-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--md); }\n .pf-c-divider.pf-m-inset-lg-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--lg); }\n .pf-c-divider.pf-m-inset-xl-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xl); }\n .pf-c-divider.pf-m-inset-2xl-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--2xl); }\n .pf-c-divider.pf-m-inset-3xl-on-xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--3xl); } }\n @media (min-width: 1450px) {\n .pf-c-divider.pf-m-inset-none-on-2xl {\n --pf-c-divider--after--Inset: 0%; }\n .pf-c-divider.pf-m-inset-xs-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xs); }\n .pf-c-divider.pf-m-inset-sm-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--sm); }\n .pf-c-divider.pf-m-inset-md-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--md); }\n .pf-c-divider.pf-m-inset-lg-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--lg); }\n .pf-c-divider.pf-m-inset-xl-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--xl); }\n .pf-c-divider.pf-m-inset-2xl-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--2xl); }\n .pf-c-divider.pf-m-inset-3xl-on-2xl {\n --pf-c-divider--after--Inset: var(--pf-global--spacer--3xl); } }\n\n.pf-c-drawer {\n --pf-c-drawer__section--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-drawer__content--FlexBasis: 100%;\n --pf-c-drawer__content--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-drawer__content--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-drawer__panel--FlexBasis: 100%;\n --pf-c-drawer__panel--md--FlexBasis: 50%;\n --pf-c-drawer__panel--MinWidth: 50%;\n --pf-c-drawer__panel--MaxHeight: auto;\n --pf-c-drawer--m-panel-bottom__panel--md--MinHeight: 50%;\n --pf-c-drawer__panel--xl--MinWidth: 28.125rem;\n --pf-c-drawer__panel--xl--FlexBasis: 28.125rem;\n --pf-c-drawer--m-panel-bottom__panel--xl--MinHeight: 18.75rem;\n --pf-c-drawer--m-panel-bottom__panel--xl--FlexBasis: 18.75rem;\n --pf-c-drawer__panel--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-drawer__panel--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-drawer__panel--TransitionDuration: var(--pf-global--TransitionDuration);\n --pf-c-drawer__panel--TransitionProperty: margin, transform, box-shadow, flex-basis;\n --pf-c-drawer__panel--m-resizable--PaddingLeft: var(--pf-c-drawer__splitter--m-vertical--Width);\n --pf-c-drawer--m-panel-left__panel--m-resizable--PaddingRight: var(--pf-c-drawer__splitter--m-vertical--Width);\n --pf-c-drawer--m-panel-bottom__panel--m-resizable--PaddingTop: var(--pf-c-drawer__splitter--Height);\n --pf-c-drawer--child--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-drawer--child--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-drawer--child--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-drawer--child--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-drawer--child--md--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--md--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--md--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--md--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--m-padding--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-drawer--child--m-padding--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-drawer--child--m-padding--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-drawer--child--m-padding--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-drawer--child--m-padding--md--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--m-padding--md--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--m-padding--md--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-drawer--child--m-padding--md--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-drawer__content--child--PaddingTop: 0;\n --pf-c-drawer__content--child--PaddingRight: 0;\n --pf-c-drawer__content--child--PaddingBottom: 0;\n --pf-c-drawer__content--child--PaddingLeft: 0;\n --pf-c-drawer__splitter--Top: 0;\n --pf-c-drawer__splitter--Right: auto;\n --pf-c-drawer__splitter--Bottom: 0;\n --pf-c-drawer__splitter--Left: 0;\n --pf-c-drawer__splitter--Height: 0.5625rem;\n --pf-c-drawer__splitter--Width: 100%;\n --pf-c-drawer__splitter--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-drawer__splitter--Cursor: row-resize;\n --pf-c-drawer__splitter--m-vertical--Height: 100%;\n --pf-c-drawer__splitter--m-vertical--Width: 0.5625rem;\n --pf-c-drawer__splitter--m-vertical--Cursor: col-resize;\n --pf-c-drawer--m-inline__splitter--focus--OutlineOffset: -0.0625rem;\n --pf-c-drawer__splitter--after--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-drawer__splitter--after--border-width--base: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer__splitter--after--BorderTopWidth: 0;\n --pf-c-drawer__splitter--after--BorderRightWidth: var(--pf-c-drawer__splitter--after--border-width--base);\n --pf-c-drawer__splitter--after--BorderBottomWidth: 0;\n --pf-c-drawer__splitter--after--BorderLeftWidth: 0;\n --pf-c-drawer--m-panel-left__splitter--after--BorderLeftWidth: var(--pf-c-drawer__splitter--after--border-width--base);\n --pf-c-drawer--m-panel-bottom__splitter--after--BorderBottomWidth: var(--pf-c-drawer__splitter--after--border-width--base);\n --pf-c-drawer--m-inline__splitter--m-vertical--Width: 0.625rem;\n --pf-c-drawer--m-inline__splitter-handle--Left: 50%;\n --pf-c-drawer--m-inline__splitter--after--BorderRightWidth: var(--pf-c-drawer__splitter--after--border-width--base);\n --pf-c-drawer--m-inline__splitter--after--BorderLeftWidth: var(--pf-c-drawer__splitter--after--border-width--base);\n --pf-c-drawer--m-inline--m-panel-bottom__splitter--Height: 0.625rem;\n --pf-c-drawer--m-inline--m-panel-bottom__splitter-handle--Top: 50%;\n --pf-c-drawer--m-inline--m-panel-bottom__splitter--after--BorderTopWidth: var(--pf-c-drawer__splitter--after--border-width--base);\n --pf-c-drawer__splitter-handle--Top: 50%;\n --pf-c-drawer__splitter-handle--Left: calc(50% - var(--pf-c-drawer__splitter--after--border-width--base));\n --pf-c-drawer--m-panel-left__splitter-handle--Left: 50%;\n --pf-c-drawer--m-panel-bottom__splitter-handle--Top: calc(50% - var(--pf-c-drawer__splitter--after--border-width--base));\n --pf-c-drawer__splitter-handle--after--BorderColor: var(--pf-global--Color--200);\n --pf-c-drawer__splitter-handle--after--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer__splitter-handle--after--BorderRightWidth: 0;\n --pf-c-drawer__splitter-handle--after--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer__splitter-handle--after--BorderLeftWidth: 0;\n --pf-c-drawer__splitter--hover__splitter-handle--after--BorderColor: var(--pf-global--Color--100);\n --pf-c-drawer__splitter--focus__splitter-handle--after--BorderColor: var(--pf-global--Color--100);\n --pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderTopWidth: 0;\n --pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderRightWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderBottomWidth: 0;\n --pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderLeftWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer__splitter-handle--after--Width: 0.75rem;\n --pf-c-drawer__splitter-handle--after--Height: 0.25rem;\n --pf-c-drawer__splitter--m-vertical__splitter-handle--after--Width: 0.25rem;\n --pf-c-drawer__splitter--m-vertical__splitter-handle--after--Height: 0.75rem;\n --pf-c-drawer__actions--MarginTop: calc(var(pf-global--spacer--form-element) * -1);\n --pf-c-drawer__actions--MarginRight: calc(var(pf-global--spacer--form-element) * -1);\n --pf-c-drawer__panel--BoxShadow: none;\n --pf-c-drawer--m-expanded__panel--BoxShadow: var(--pf-global--BoxShadow--lg-left);\n --pf-c-drawer--m-expanded--m-panel-left__panel--BoxShadow: var(--pf-global--BoxShadow--lg-right);\n --pf-c-drawer--m-expanded--m-panel-bottom__panel--BoxShadow: var(--pf-global--BoxShadow--lg-top);\n --pf-c-drawer__panel--after--Width: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer--m-panel-bottom__panel--after--Height: var(--pf-global--BorderWidth--sm);\n --pf-c-drawer__panel--after--BackgroundColor: transparent;\n --pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor: var(--pf-global--BorderColor--100);\n --pf-c-drawer--m-inline__panel--PaddingLeft: var(--pf-c-drawer__panel--after--Width);\n --pf-c-drawer--m-panel-left--m-inline__panel--PaddingRight: var(--pf-c-drawer__panel--after--Width);\n --pf-c-drawer--m-panel-bottom--m-inline__panel--PaddingTop: var(--pf-c-drawer__panel--after--Width);\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow-x: hidden; }\n @media screen and (min-width: 768px) {\n .pf-c-drawer {\n --pf-c-drawer__panel--FlexBasis: var(--pf-c-drawer__panel--md--FlexBasis);\n --pf-c-drawer--child--PaddingTop: var(--pf-c-drawer--child--md--PaddingTop);\n --pf-c-drawer--child--PaddingRight: var(--pf-c-drawer--child--md--PaddingRight);\n --pf-c-drawer--child--PaddingBottom: var(--pf-c-drawer--child--md--PaddingBottom);\n --pf-c-drawer--child--PaddingLeft: var(--pf-c-drawer--child--md--PaddingLeft);\n --pf-c-drawer--child--m-padding--PaddingTop: var(--pf-c-drawer--child--m-padding--md--PaddingTop);\n --pf-c-drawer--child--m-padding--PaddingRight: var(--pf-c-drawer--child--m-padding--md--PaddingRight);\n --pf-c-drawer--child--m-padding--PaddingBottom: var(--pf-c-drawer--child--m-padding--md--PaddingBottom);\n --pf-c-drawer--child--m-padding--PaddingLeft: var(--pf-c-drawer--child--m-padding--md--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-drawer {\n --pf-c-drawer__panel--FlexBasis: var(--pf-c-drawer__panel--xl--FlexBasis);\n --pf-c-drawer__panel--MinWidth: var(--pf-c-drawer__panel--xl--MinWidth); }\n .pf-c-drawer.pf-m-panel-bottom {\n --pf-c-drawer__panel--MinWidth: auto;\n --pf-c-drawer__panel--FlexBasis: var(--pf-c-drawer--m-panel-bottom__panel--xl--FlexBasis);\n --pf-c-drawer__panel--MinHeight: var(--pf-c-drawer--m-panel-bottom__panel--xl--MinHeight); } }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel , .pf-c-drawer.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel {\n padding-left: var(--pf-c-drawer--m-inline__panel--PaddingLeft); }\n .pf-c-drawer.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n order: 0;\n margin-right: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n transform: translateX(-100%); }\n .pf-c-drawer.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__content {\n order: 1; }\n .pf-c-drawer.pf-m-panel-bottom > .pf-c-drawer__main {\n flex-direction: column; }\n .pf-c-drawer.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(-100%); }\n .pf-c-drawer.pf-m-expanded.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-expanded.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translate(0, -100%); }\n\n.pf-c-drawer__section {\n flex-grow: 0;\n background-color: var(--pf-c-drawer__section--BackgroundColor); }\n .pf-c-drawer__section.pf-m-no-background {\n background-color: transparent; }\n\n.pf-c-drawer__main {\n display: flex;\n flex-grow: 1;\n overflow: hidden; }\n\n.pf-c-drawer__content,\n.pf-c-drawer__panel {\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n overflow: auto; }\n\n.pf-c-drawer__content {\n z-index: var(--pf-c-drawer__content--ZIndex);\n flex-basis: var(--pf-c-drawer__content--FlexBasis);\n order: 0;\n background-color: var(--pf-c-drawer__content--BackgroundColor); }\n .pf-c-drawer__content > .pf-c-drawer__body {\n padding: var(--pf-c-drawer__content--child--PaddingTop) var(--pf-c-drawer__content--child--PaddingRight) var(--pf-c-drawer__content--child--PaddingBottom) var(--pf-c-drawer__content--child--PaddingLeft); }\n .pf-c-drawer__content.pf-m-no-background {\n background-color: transparent; }\n\n.pf-c-drawer__panel {\n position: relative;\n z-index: var(--pf-c-drawer__panel--ZIndex);\n flex-basis: var(--pf-c-drawer__panel--FlexBasis);\n order: 1;\n max-height: var(--pf-c-drawer__panel--MaxHeight);\n overflow: auto;\n background-color: var(--pf-c-drawer__panel--BackgroundColor);\n box-shadow: var(--pf-c-drawer__panel--BoxShadow);\n transition-duration: var(--pf-c-drawer__panel--TransitionDuration);\n transition-property: var(--pf-c-drawer__panel--TransitionProperty);\n -webkit-overflow-scrolling: touch; }\n .pf-c-drawer__panel::after {\n position: absolute;\n top: 0;\n left: 0;\n width: var(--pf-c-drawer__panel--after--Width);\n height: 100%;\n content: \"\";\n background-color: var(--pf-c-drawer__panel--after--BackgroundColor); }\n .pf-c-drawer__panel.pf-m-no-background {\n background-color: transparent; }\n\n@keyframes pf-remove-tab-focus {\n to {\n visibility: hidden; } }\n\n.pf-c-drawer__panel[hidden] {\n animation-name: pf-remove-tab-focus;\n animation-delay: var(--pf-c-drawer__panel--TransitionDuration);\n animation-fill-mode: forwards; }\n\n.pf-c-drawer__head {\n display: grid;\n grid-template-columns: auto;\n grid-auto-columns: max-content; }\n .pf-c-drawer__head > * {\n grid-column: 1; }\n\n.pf-c-drawer__actions {\n grid-column: 2;\n grid-row: 1;\n display: flex;\n align-self: baseline;\n margin-top: var(--pf-c-drawer__actions--MarginTop);\n margin-right: var(--pf-c-drawer__actions--MarginRight); }\n\n.pf-c-drawer__body {\n min-height: 0;\n padding: var(--pf-c-drawer--child--PaddingTop) var(--pf-c-drawer--child--PaddingRight) var(--pf-c-drawer--child--PaddingBottom) var(--pf-c-drawer--child--PaddingLeft); }\n .pf-c-drawer__body.pf-m-no-padding {\n padding: 0; }\n .pf-c-drawer__body.pf-m-no-padding > .pf-c-drawer__actions,\n .pf-c-drawer__body.pf-m-no-padding > .pf-c-drawer__head > .pf-c-drawer__actions {\n margin-top: 0;\n margin-right: 0; }\n .pf-c-drawer__body.pf-m-padding {\n padding: var(--pf-c-drawer--child--m-padding--PaddingTop) var(--pf-c-drawer--child--m-padding--PaddingRight) var(--pf-c-drawer--child--m-padding--PaddingBottom) var(--pf-c-drawer--child--m-padding--PaddingLeft); }\n .pf-c-drawer__body:not(.pf-m-no-padding) + * {\n padding-top: 0; }\n .pf-c-drawer__body:last-child {\n flex: 1 1; }\n\n.pf-c-drawer__body > .pf-c-page__main {\n min-height: 100%; }\n\n.pf-c-drawer__splitter {\n position: absolute;\n top: var(--pf-c-drawer__splitter--Top);\n right: var(--pf-c-drawer__splitter--Right);\n bottom: var(--pf-c-drawer__splitter--Bottom);\n left: var(--pf-c-drawer__splitter--Left);\n display: none;\n width: var(--pf-c-drawer__splitter--Width);\n height: var(--pf-c-drawer__splitter--Height);\n cursor: var(--pf-c-drawer__splitter--Cursor);\n visibility: hidden;\n background-color: var(--pf-c-drawer__splitter--BackgroundColor); }\n .pf-c-drawer__splitter.pf-m-vertical {\n --pf-c-drawer__splitter--Height: var(--pf-c-drawer__splitter--m-vertical--Height);\n --pf-c-drawer__splitter--Width: var(--pf-c-drawer__splitter--m-vertical--Width);\n --pf-c-drawer__splitter--Cursor: var(--pf-c-drawer__splitter--m-vertical--Cursor);\n --pf-c-drawer__splitter-handle--after--Width: var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--Width);\n --pf-c-drawer__splitter-handle--after--Height: var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--Height);\n --pf-c-drawer__splitter-handle--after--BorderTopWidth: var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderTopWidth);\n --pf-c-drawer__splitter-handle--after--BorderRightWidth: var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderRightWidth);\n --pf-c-drawer__splitter-handle--after--BorderBottomWidth: var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderBottomWidth);\n --pf-c-drawer__splitter-handle--after--BorderLeftWidth: var(--pf-c-drawer__splitter--m-vertical__splitter-handle--after--BorderLeftWidth); }\n .pf-c-drawer__splitter:hover {\n --pf-c-drawer__splitter-handle--after--BorderColor: var(--pf-c-drawer__splitter--hover__splitter-handle--after--BorderColor); }\n .pf-c-drawer__splitter:focus {\n --pf-c-drawer__splitter-handle--after--BorderColor: var(--pf-c-drawer__splitter--focus__splitter-handle--after--BorderColor); }\n .pf-c-drawer__splitter::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: solid var(--pf-c-drawer__splitter--after--BorderColor);\n border-width: var(--pf-c-drawer__splitter--after--BorderTopWidth) var(--pf-c-drawer__splitter--after--BorderRightWidth) var(--pf-c-drawer__splitter--after--BorderBottomWidth) var(--pf-c-drawer__splitter--after--BorderLeftWidth); }\n\n.pf-c-drawer__splitter-handle {\n position: absolute;\n top: var(--pf-c-drawer__splitter-handle--Top);\n left: var(--pf-c-drawer__splitter-handle--Left);\n transform: translate(-50%, -50%); }\n .pf-c-drawer__splitter-handle::after {\n display: block;\n width: var(--pf-c-drawer__splitter-handle--after--Width);\n height: var(--pf-c-drawer__splitter-handle--after--Height);\n content: \"\";\n border-color: var(--pf-c-drawer__splitter-handle--after--BorderColor);\n border-style: solid;\n border-width: var(--pf-c-drawer__splitter-handle--after--BorderTopWidth) var(--pf-c-drawer__splitter-handle--after--BorderRightWidth) var(--pf-c-drawer__splitter-handle--after--BorderBottomWidth) var(--pf-c-drawer__splitter-handle--after--BorderLeftWidth); }\n\n@media screen and (min-width: 768px) {\n .pf-c-drawer {\n min-width: var(--pf-c-drawer__panel--MinWidth); }\n .pf-c-drawer.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n box-shadow: var(--pf-c-drawer--m-expanded__panel--BoxShadow); }\n .pf-c-drawer > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable {\n padding-left: var(--pf-c-drawer__panel--m-resizable--PaddingLeft); }\n .pf-c-drawer > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable::after {\n width: 0;\n height: 0; }\n .pf-c-drawer.pf-m-panel-left {\n --pf-c-drawer--m-expanded__panel--BoxShadow: var(--pf-c-drawer--m-expanded--m-panel-left__panel--BoxShadow); }\n .pf-c-drawer.pf-m-panel-left.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel , .pf-c-drawer.pf-m-panel-left.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel {\n padding-right: var(--pf-c-drawer--m-panel-left--m-inline__panel--PaddingRight);\n padding-left: 0; }\n .pf-c-drawer.pf-m-panel-left.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel::after {\n right: 0;\n left: auto; }\n .pf-c-drawer.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable {\n padding-right: var(--pf-c-drawer--m-panel-left__panel--m-resizable--PaddingRight);\n padding-left: 0; }\n .pf-c-drawer.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable > .pf-c-drawer__splitter {\n --pf-c-drawer__splitter--Right: 0;\n --pf-c-drawer__splitter--Left: auto;\n --pf-c-drawer__splitter-handle--Left: var(--pf-c-drawer--m-panel-left__splitter-handle--Left);\n --pf-c-drawer__splitter--after--BorderRightWidth: 0;\n --pf-c-drawer__splitter--after--BorderLeftWidth: var(--pf-c-drawer--m-panel-left__splitter--after--BorderLeftWidth); }\n .pf-c-drawer.pf-m-panel-bottom {\n --pf-c-drawer--m-expanded__panel--BoxShadow: var(--pf-c-drawer--m-expanded--m-panel-bottom__panel--BoxShadow);\n --pf-c-drawer__panel--MaxHeight: 100%;\n min-width: auto;\n min-height: var(--pf-c-drawer--m-panel-bottom__panel--md--MinHeight); }\n .pf-c-drawer.pf-m-panel-bottom.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel , .pf-c-drawer.pf-m-panel-bottom.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel {\n padding-top: var(--pf-c-drawer--m-panel-bottom--m-inline__panel--PaddingTop);\n padding-left: 0; }\n .pf-c-drawer.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel::after {\n top: 0;\n left: auto;\n width: 100%;\n height: var(--pf-c-drawer--m-panel-bottom__panel--after--Height); }\n .pf-c-drawer.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable {\n padding-top: var(--pf-c-drawer--m-panel-bottom__panel--m-resizable--PaddingTop);\n padding-left: 0; }\n .pf-c-drawer.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable > .pf-c-drawer__splitter {\n --pf-c-drawer__splitter--Top: 0;\n --pf-c-drawer__splitter--Right: 0;\n --pf-c-drawer__splitter--Bottom: auto;\n --pf-c-drawer__splitter-handle--Top: var(--pf-c-drawer--m-panel-bottom__splitter-handle--Top);\n --pf-c-drawer__splitter--after--BorderRightWidth: 0;\n --pf-c-drawer__splitter--after--BorderBottomWidth: var(--pf-c-drawer--m-panel-bottom__splitter--after--BorderBottomWidth); }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable > .pf-c-drawer__splitter {\n --pf-c-drawer__splitter--m-vertical--Width: var(--pf-c-drawer--m-inline__splitter--m-vertical--Width);\n --pf-c-drawer__splitter-handle--Left: var(--pf-c-drawer--m-inline__splitter-handle--Left);\n --pf-c-drawer__splitter--after--BorderRightWidth: var(--pf-c-drawer--m-inline__splitter--after--BorderRightWidth);\n --pf-c-drawer__splitter--after--BorderLeftWidth: var(--pf-c-drawer--m-inline__splitter--after--BorderLeftWidth);\n outline-offset: var(--pf-c-drawer--m-inline__splitter--focus--OutlineOffset); }\n .pf-c-drawer.pf-m-inline.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-resizable > .pf-c-drawer__splitter {\n --pf-c-drawer__splitter--Height: var(--pf-c-drawer--m-inline--m-panel-bottom__splitter--Height);\n --pf-c-drawer__splitter-handle--Top: var(--pf-c-drawer--m-inline--m-panel-bottom__splitter-handle--Top);\n --pf-c-drawer__splitter--after--BorderTopWidth: var(--pf-c-drawer--m-inline--m-panel-bottom__splitter--after--BorderTopWidth);\n --pf-c-drawer__splitter--after--BorderRightWidth: 0;\n --pf-c-drawer__splitter--after--BorderLeftWidth: 0; }\n .pf-c-drawer > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-no-border,\n .pf-c-drawer.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel.pf-m-no-border {\n --pf-c-drawer--m-expanded__panel--BoxShadow: none; }\n .pf-c-drawer__splitter {\n display: block;\n visibility: visible; } }\n\n@media (min-width: 768px) {\n .pf-c-drawer__panel.pf-m-width-25 {\n --pf-c-drawer__panel--FlexBasis: 25%; }\n .pf-c-drawer__panel.pf-m-width-33 {\n --pf-c-drawer__panel--FlexBasis: 33%; }\n .pf-c-drawer__panel.pf-m-width-50 {\n --pf-c-drawer__panel--FlexBasis: 50%; }\n .pf-c-drawer__panel.pf-m-width-66 {\n --pf-c-drawer__panel--FlexBasis: 66%; }\n .pf-c-drawer__panel.pf-m-width-75 {\n --pf-c-drawer__panel--FlexBasis: 75%; }\n .pf-c-drawer__panel.pf-m-width-100 {\n --pf-c-drawer__panel--FlexBasis: 100%; } }\n\n@media (min-width: 992px) {\n .pf-c-drawer__panel.pf-m-width-25-on-lg {\n --pf-c-drawer__panel--FlexBasis: 25%; }\n .pf-c-drawer__panel.pf-m-width-33-on-lg {\n --pf-c-drawer__panel--FlexBasis: 33%; }\n .pf-c-drawer__panel.pf-m-width-50-on-lg {\n --pf-c-drawer__panel--FlexBasis: 50%; }\n .pf-c-drawer__panel.pf-m-width-66-on-lg {\n --pf-c-drawer__panel--FlexBasis: 66%; }\n .pf-c-drawer__panel.pf-m-width-75-on-lg {\n --pf-c-drawer__panel--FlexBasis: 75%; }\n .pf-c-drawer__panel.pf-m-width-100-on-lg {\n --pf-c-drawer__panel--FlexBasis: 100%; } }\n\n@media (min-width: 1200px) {\n .pf-c-drawer__panel.pf-m-width-25-on-xl {\n --pf-c-drawer__panel--FlexBasis: 25%; }\n .pf-c-drawer__panel.pf-m-width-33-on-xl {\n --pf-c-drawer__panel--FlexBasis: 33%; }\n .pf-c-drawer__panel.pf-m-width-50-on-xl {\n --pf-c-drawer__panel--FlexBasis: 50%; }\n .pf-c-drawer__panel.pf-m-width-66-on-xl {\n --pf-c-drawer__panel--FlexBasis: 66%; }\n .pf-c-drawer__panel.pf-m-width-75-on-xl {\n --pf-c-drawer__panel--FlexBasis: 75%; }\n .pf-c-drawer__panel.pf-m-width-100-on-xl {\n --pf-c-drawer__panel--FlexBasis: 100%; } }\n\n@media (min-width: 1450px) {\n .pf-c-drawer__panel.pf-m-width-25-on-2xl {\n --pf-c-drawer__panel--FlexBasis: 25%; }\n .pf-c-drawer__panel.pf-m-width-33-on-2xl {\n --pf-c-drawer__panel--FlexBasis: 33%; }\n .pf-c-drawer__panel.pf-m-width-50-on-2xl {\n --pf-c-drawer__panel--FlexBasis: 50%; }\n .pf-c-drawer__panel.pf-m-width-66-on-2xl {\n --pf-c-drawer__panel--FlexBasis: 66%; }\n .pf-c-drawer__panel.pf-m-width-75-on-2xl {\n --pf-c-drawer__panel--FlexBasis: 75%; }\n .pf-c-drawer__panel.pf-m-width-100-on-2xl {\n --pf-c-drawer__panel--FlexBasis: 100%; } }\n\n@media (min-width: 768px) {\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__content,\n .pf-c-drawer.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__content {\n flex-shrink: 1; }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel,\n .pf-c-drawer.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel {\n --pf-c-drawer--m-expanded__panel--BoxShadow: none; }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after,\n .pf-c-drawer.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after {\n background-color: var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor); }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__content {\n overflow-x: auto; }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n transform: translateX(100%); }\n .pf-c-drawer.pf-m-inline.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-inline.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n margin-left: 0;\n transform: translateX(-100%); }\n .pf-c-drawer.pf-m-inline.pf-m-panel-left.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 992px) {\n .pf-c-drawer.pf-m-inline-on-lg > .pf-c-drawer__main > .pf-c-drawer__content,\n .pf-c-drawer.pf-m-static-on-lg > .pf-c-drawer__main > .pf-c-drawer__content {\n flex-shrink: 1; }\n .pf-c-drawer.pf-m-inline-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel,\n .pf-c-drawer.pf-m-static-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel {\n --pf-c-drawer--m-expanded__panel--BoxShadow: none; }\n .pf-c-drawer.pf-m-inline-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after,\n .pf-c-drawer.pf-m-static-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after {\n background-color: var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor); }\n .pf-c-drawer.pf-m-inline-on-lg > .pf-c-drawer__main > .pf-c-drawer__content {\n overflow-x: auto; }\n .pf-c-drawer.pf-m-inline-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n transform: translateX(100%); }\n .pf-c-drawer.pf-m-inline-on-lg.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-inline-on-lg.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n margin-left: 0;\n transform: translateX(-100%); }\n .pf-c-drawer.pf-m-inline-on-lg.pf-m-panel-left.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline-on-lg.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-static-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-lg.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-lg.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-lg > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 1200px) {\n .pf-c-drawer.pf-m-inline-on-xl > .pf-c-drawer__main > .pf-c-drawer__content,\n .pf-c-drawer.pf-m-static-on-xl > .pf-c-drawer__main > .pf-c-drawer__content {\n flex-shrink: 1; }\n .pf-c-drawer.pf-m-inline-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel,\n .pf-c-drawer.pf-m-static-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel {\n --pf-c-drawer--m-expanded__panel--BoxShadow: none; }\n .pf-c-drawer.pf-m-inline-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after,\n .pf-c-drawer.pf-m-static-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after {\n background-color: var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor); }\n .pf-c-drawer.pf-m-inline-on-xl > .pf-c-drawer__main > .pf-c-drawer__content {\n overflow-x: auto; }\n .pf-c-drawer.pf-m-inline-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n transform: translateX(100%); }\n .pf-c-drawer.pf-m-inline-on-xl.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-inline-on-xl.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n margin-left: 0;\n transform: translateX(-100%); }\n .pf-c-drawer.pf-m-inline-on-xl.pf-m-panel-left.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline-on-xl.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-static-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-xl.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-xl.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-xl > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: none;\n visibility: hidden; } }\n\n@media (min-width: 1450px) {\n .pf-c-drawer.pf-m-inline-on-2xl > .pf-c-drawer__main > .pf-c-drawer__content,\n .pf-c-drawer.pf-m-static-on-2xl > .pf-c-drawer__main > .pf-c-drawer__content {\n flex-shrink: 1; }\n .pf-c-drawer.pf-m-inline-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel,\n .pf-c-drawer.pf-m-static-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel {\n --pf-c-drawer--m-expanded__panel--BoxShadow: none; }\n .pf-c-drawer.pf-m-inline-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after,\n .pf-c-drawer.pf-m-static-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel:not(.pf-m-no-border)::after {\n background-color: var(--pf-c-drawer--m-inline--m-expanded__panel--after--BackgroundColor); }\n .pf-c-drawer.pf-m-inline-on-2xl > .pf-c-drawer__main > .pf-c-drawer__content {\n overflow-x: auto; }\n .pf-c-drawer.pf-m-inline-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n transform: translateX(100%); }\n .pf-c-drawer.pf-m-inline-on-2xl.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-left: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-inline-on-2xl.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: calc(var(--pf-c-drawer__panel--FlexBasis) * -1);\n margin-left: 0;\n transform: translateX(-100%); }\n .pf-c-drawer.pf-m-inline-on-2xl.pf-m-panel-left.pf-m-expanded > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-inline-on-2xl.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: unset;\n visibility: visible; }\n .pf-c-drawer.pf-m-static-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-2xl.pf-m-panel-left > .pf-c-drawer__main > .pf-c-drawer__panel {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-2xl.pf-m-panel-bottom > .pf-c-drawer__main > .pf-c-drawer__panel {\n transform: translateX(0); }\n .pf-c-drawer.pf-m-static-on-2xl > .pf-c-drawer__main > .pf-c-drawer__panel > .pf-c-drawer__body > .pf-c-drawer__head .pf-c-drawer__close {\n display: none;\n visibility: hidden; } }\n\n.pf-c-dropdown {\n --pf-c-dropdown__toggle--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-dropdown__toggle--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-dropdown__toggle--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle--MinWidth: var(--pf-global--target-size--MinWidth);\n --pf-c-dropdown__toggle--FontSize: var(--pf-global--FontSize--md);\n --pf-c-dropdown__toggle--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-dropdown__toggle--Color: var(--pf-global--Color--100);\n --pf-c-dropdown__toggle--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-dropdown__toggle--BackgroundColor: transparent;\n --pf-c-dropdown__toggle--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-dropdown__toggle--before--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-dropdown__toggle--before--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-dropdown__toggle--before--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-dropdown__toggle--before--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-dropdown__toggle--hover--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-dropdown__toggle--active--before--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-dropdown__toggle--active--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-dropdown__toggle--focus--before--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-dropdown__toggle--focus--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-dropdown--m-expanded__toggle--before--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-dropdown--m-expanded__toggle--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-dropdown__toggle--disabled--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-dropdown__toggle--m-plain--Color: var(--pf-global--Color--200);\n --pf-c-dropdown__toggle--m-plain--hover--Color: var(--pf-global--Color--100);\n --pf-c-dropdown__toggle--m-plain--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-dropdown__toggle--m-plain--child--LineHeight: normal;\n --pf-c-dropdown__toggle--m-primary--Color: var(--pf-global--Color--light-100);\n --pf-c-dropdown__toggle--m-primary--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-dropdown__toggle--m-primary--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-dropdown__toggle--m-primary--hover--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-dropdown__toggle--m-primary--active--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-dropdown__toggle--m-primary--focus--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-dropdown--m-expanded__toggle--m-primary--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-dropdown__toggle-button--Color: var(--pf-global--Color--100);\n --pf-c-dropdown__toggle--m-split-button--child--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-dropdown__toggle--m-split-button--child--PaddingRight: var(--pf-global--spacer--xs);\n --pf-c-dropdown__toggle--m-split-button--child--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-dropdown__toggle--m-split-button--child--PaddingLeft: var(--pf-global--spacer--xs);\n --pf-c-dropdown__toggle--m-split-button--child--BackgroundColor: transparent;\n --pf-c-dropdown__toggle--m-split-button--first-child--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle--m-split-button--last-child--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle--m-split-button--m-action__toggle-button--MarginRight: calc(-1 * var(--pf-global--BorderWidth--sm));\n --pf-c-dropdown__toggle--m-split-button__toggle-check__input--TranslateY: -0.0625rem;\n --pf-c-dropdown__toggle--m-split-button__toggle-text--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle-icon--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-dropdown__toggle-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-dropdown__toggle-icon--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-dropdown--m-top--m-expanded__toggle-icon--Rotate: 180deg;\n --pf-c-dropdown__menu--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-dropdown__menu--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-dropdown__menu--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-dropdown__menu--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-dropdown__menu--Top: calc(100% + var(--pf-global--spacer--xs));\n --pf-c-dropdown__menu--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-dropdown--m-top__menu--Top: 0;\n --pf-c-dropdown--m-top__menu--TranslateY: calc(-100% - var(--pf-global--spacer--xs));\n --pf-c-dropdown__menu-item--BackgroundColor: transparent;\n --pf-c-dropdown__menu-item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-dropdown__menu-item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-dropdown__menu-item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-dropdown__menu-item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-dropdown__menu-item--FontSize: var(--pf-global--FontSize--md);\n --pf-c-dropdown__menu-item--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-dropdown__menu-item--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-dropdown__menu-item--Color: var(--pf-global--Color--dark-100);\n --pf-c-dropdown__menu-item--hover--Color: var(--pf-global--Color--dark-100);\n --pf-c-dropdown__menu-item--disabled--Color: var(--pf-global--Color--dark-200);\n --pf-c-dropdown__menu-item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-dropdown__menu-item--disabled--BackgroundColor: transparent;\n --pf-c-dropdown__menu-item--m-text--Color: var(--pf-global--Color--dark-200);\n --pf-c-dropdown__menu-item-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-dropdown__menu-item-icon--Width: var(--pf-global--icon--FontSize--lg);\n --pf-c-dropdown__menu-item-icon--Height: var(--pf-global--icon--FontSize--lg);\n --pf-c-dropdown__menu-item-description--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-dropdown__menu-item-description--Color: var(--pf-global--Color--dark-200);\n --pf-c-dropdown__group--group--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-dropdown__group-title--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-dropdown__group-title--PaddingRight: var(--pf-c-dropdown__menu-item--PaddingRight);\n --pf-c-dropdown__group-title--PaddingBottom: var(--pf-c-dropdown__menu-item--PaddingBottom);\n --pf-c-dropdown__group-title--PaddingLeft: var(--pf-c-dropdown__menu-item--PaddingLeft);\n --pf-c-dropdown__group-title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-dropdown__group-title--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-dropdown__group-title--Color: var(--pf-global--Color--dark-200);\n --pf-c-dropdown__toggle-image--MarginTop: var(--pf-global--spacer--xs);\n --pf-c-dropdown__toggle-image--MarginBottom: var(--pf-global--spacer--xs);\n --pf-c-dropdown__toggle-image--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-dropdown--c-divider--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-dropdown--c-divider--MarginBottom: var(--pf-global--spacer--sm);\n position: relative;\n display: inline-block;\n max-width: 100%; }\n .pf-c-dropdown .pf-c-divider {\n margin-top: var(--pf-c-dropdown--c-divider--MarginTop);\n margin-bottom: var(--pf-c-dropdown--c-divider--MarginBottom); }\n .pf-c-dropdown .pf-c-divider:last-child {\n --pf-c-dropdown--c-divider--MarginBottom: 0; }\n\n.pf-c-dropdown__toggle {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-width: var(--pf-c-dropdown__toggle--MinWidth);\n max-width: 100%;\n padding: var(--pf-c-dropdown__toggle--PaddingTop) var(--pf-c-dropdown__toggle--PaddingRight) var(--pf-c-dropdown__toggle--PaddingBottom) var(--pf-c-dropdown__toggle--PaddingLeft);\n font-size: var(--pf-c-dropdown__toggle--FontSize);\n font-weight: var(--pf-c-dropdown__toggle--FontWeight);\n line-height: var(--pf-c-dropdown__toggle--LineHeight);\n color: var(--pf-c-dropdown__toggle--Color);\n background-color: var(--pf-c-dropdown__toggle--BackgroundColor);\n border: none; }\n .pf-c-dropdown__toggle::before,\n .pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-dropdown__toggle--before--BorderWidth) solid;\n border-color: var(--pf-c-dropdown__toggle--before--BorderTopColor) var(--pf-c-dropdown__toggle--before--BorderRightColor) var(--pf-c-dropdown__toggle--before--BorderBottomColor) var(--pf-c-dropdown__toggle--before--BorderLeftColor); }\n .pf-c-dropdown__toggle.pf-m-disabled, .pf-c-dropdown__toggle:disabled {\n pointer-events: none; }\n .pf-c-dropdown__toggle.pf-m-disabled:not(.pf-m-plain), .pf-c-dropdown__toggle:disabled:not(.pf-m-plain) {\n --pf-c-dropdown__toggle--BackgroundColor: var(--pf-c-dropdown__toggle--disabled--BackgroundColor); }\n .pf-c-dropdown__toggle.pf-m-disabled:not(.pf-m-plain)::before, .pf-c-dropdown__toggle:disabled:not(.pf-m-plain)::before {\n border: 0; }\n .pf-c-dropdown__toggle.pf-m-split-button {\n padding: 0; }\n .pf-c-dropdown__toggle.pf-m-split-button > * {\n position: relative;\n padding-top: var(--pf-c-dropdown__toggle--m-split-button--child--PaddingTop);\n padding-right: var(--pf-c-dropdown__toggle--m-split-button--child--PaddingRight);\n padding-bottom: var(--pf-c-dropdown__toggle--m-split-button--child--PaddingBottom);\n padding-left: var(--pf-c-dropdown__toggle--m-split-button--child--PaddingLeft);\n background-color: var(--pf-c-dropdown__toggle--m-split-button--child--BackgroundColor); }\n .pf-c-dropdown__toggle.pf-m-split-button > *:first-child {\n --pf-c-dropdown__toggle--m-split-button--child--PaddingLeft: var(--pf-c-dropdown__toggle--m-split-button--first-child--PaddingLeft); }\n .pf-c-dropdown__toggle.pf-m-split-button > *:last-child {\n --pf-c-dropdown__toggle--m-split-button--child--PaddingRight: var(--pf-c-dropdown__toggle--m-split-button--last-child--PaddingRight); }\n .pf-c-dropdown__toggle.pf-m-split-button.pf-m-action {\n --pf-c-dropdown__toggle--m-split-button--child--PaddingRight: var(--pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingRight);\n --pf-c-dropdown__toggle--m-split-button--child--PaddingLeft: var(--pf-c-dropdown__toggle--m-split-button--m-action--child--PaddingLeft); }\n .pf-c-dropdown__toggle.pf-m-split-button.pf-m-action .pf-c-dropdown__toggle-button {\n margin-right: var(--pf-c-dropdown__toggle--m-split-button--m-action__toggle-button--MarginRight); }\n .pf-c-dropdown__toggle.pf-m-split-button.pf-m-action .pf-c-dropdown__toggle-button::before {\n border-left: 0; }\n .pf-c-dropdown__toggle.pf-m-split-button.pf-m-action .pf-c-dropdown__toggle-button:last-child {\n --pf-c-dropdown__toggle--m-split-button--m-action__toggle-button--MarginRight: 0; }\n .pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-check {\n display: flex;\n align-items: center;\n cursor: pointer; }\n .pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-check input {\n transform: translateY(var(--pf-c-dropdown__toggle--m-split-button__toggle-check__input--TranslateY)); }\n .pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-button {\n color: var(--pf-c-dropdown__toggle-button--Color);\n border: 0; }\n .pf-c-dropdown__toggle.pf-m-split-button .pf-c-dropdown__toggle-text {\n margin-left: var(--pf-c-dropdown__toggle--m-split-button__toggle-text--MarginLeft); }\n .pf-c-dropdown__toggle:not(.pf-m-action):hover::before,\n .pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:hover::before {\n --pf-c-dropdown__toggle--before--BorderBottomColor: var(--pf-c-dropdown__toggle--hover--before--BorderBottomColor); }\n .pf-c-dropdown__toggle:not(.pf-m-action):active::before, .pf-c-dropdown__toggle:not(.pf-m-action).pf-m-active::before,\n .pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:active::before {\n --pf-c-dropdown__toggle--before--BorderBottomColor: var(--pf-c-dropdown__toggle--active--before--BorderBottomColor);\n border-bottom-width: var(--pf-c-dropdown__toggle--active--before--BorderBottomWidth); }\n .pf-c-dropdown__toggle:not(.pf-m-action):focus::before,\n .pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button:focus::before {\n --pf-c-dropdown__toggle--before--BorderBottomColor: var(--pf-c-dropdown__toggle--focus--before--BorderBottomColor);\n border-bottom-width: var(--pf-c-dropdown__toggle--focus--before--BorderBottomWidth); }\n .pf-m-expanded > .pf-c-dropdown__toggle:not(.pf-m-action)::before,\n .pf-m-expanded > .pf-c-dropdown__toggle.pf-m-action .pf-c-dropdown__toggle-button::before {\n --pf-c-dropdown__toggle--before--BorderBottomColor: var(--pf-c-dropdown--m-expanded__toggle--before--BorderBottomColor);\n border-bottom-width: var(--pf-c-dropdown--m-expanded__toggle--before--BorderBottomWidth); }\n .pf-c-dropdown__toggle.pf-m-plain {\n display: inline-block;\n color: var(--pf-c-dropdown__toggle--m-plain--Color); }\n .pf-c-dropdown__toggle.pf-m-plain > * {\n line-height: var(--pf-c-dropdown__toggle--m-plain--child--LineHeight); }\n .pf-c-dropdown__toggle.pf-m-plain::before {\n border: 0; }\n .pf-c-dropdown__toggle.pf-m-plain:hover, .pf-c-dropdown__toggle.pf-m-plain:active, .pf-c-dropdown__toggle.pf-m-plain.pf-m-active, .pf-c-dropdown__toggle.pf-m-plain:focus,\n .pf-m-expanded > .pf-c-dropdown__toggle.pf-m-plain {\n --pf-c-dropdown__toggle--m-plain--Color: var(--pf-c-dropdown__toggle--m-plain--hover--Color); }\n .pf-c-dropdown__toggle.pf-m-plain.pf-m-disabled, .pf-c-dropdown__toggle.pf-m-plain:disabled {\n --pf-c-dropdown__toggle--m-plain--Color: var(--pf-c-dropdown__toggle--m-plain--disabled--Color); }\n .pf-c-dropdown__toggle.pf-m-primary {\n --pf-c-dropdown__toggle--Color: var(--pf-c-dropdown__toggle--m-primary--Color);\n --pf-c-dropdown__toggle--BackgroundColor: var(--pf-c-dropdown__toggle--m-primary--BackgroundColor);\n border-radius: var(--pf-c-dropdown__toggle--m-primary--BorderRadius); }\n .pf-c-dropdown__toggle.pf-m-primary::before {\n border: 0; }\n .pf-c-dropdown__toggle.pf-m-primary:hover {\n --pf-c-dropdown__toggle--BackgroundColor: var(--pf-c-dropdown__toggle--m-primary--hover--BackgroundColor); }\n .pf-c-dropdown__toggle.pf-m-primary:active, .pf-c-dropdown__toggle.pf-m-primary.pf-m-active {\n --pf-c-dropdown__toggle--BackgroundColor: var(--pf-c-dropdown__toggle--m-primary--active--BackgroundColor); }\n .pf-c-dropdown__toggle.pf-m-primary:focus {\n --pf-c-dropdown__toggle--BackgroundColor: var(--pf-c-dropdown__toggle--m-primary--focus--BackgroundColor); }\n .pf-m-expanded > .pf-c-dropdown__toggle.pf-m-primary {\n --pf-c-dropdown__toggle--BackgroundColor: var(--pf-c-dropdown--m-expanded__toggle--m-primary--BackgroundColor); }\n .pf-c-dropdown__toggle .pf-c-dropdown__toggle-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n\n.pf-c-dropdown__toggle-icon {\n margin-right: var(--pf-c-dropdown__toggle-icon--MarginRight);\n margin-left: var(--pf-c-dropdown__toggle-icon--MarginLeft);\n line-height: var(--pf-c-dropdown__toggle-icon--LineHeight); }\n .pf-c-dropdown.pf-m-top.pf-m-expanded .pf-c-dropdown__toggle-icon {\n transform: rotate(var(--pf-c-dropdown--m-top--m-expanded__toggle-icon--Rotate)); }\n\n.pf-c-dropdown__toggle-image {\n display: inline-flex;\n margin-top: var(--pf-c-dropdown__toggle-image--MarginTop);\n margin-right: var(--pf-c-dropdown__toggle-image--MarginRight);\n margin-bottom: var(--pf-c-dropdown__toggle-image--MarginBottom); }\n .pf-c-dropdown__toggle-image:last-child {\n --pf-c-dropdown__toggle-image--MarginRight: 0; }\n\n.pf-c-dropdown__menu {\n position: absolute;\n top: var(--pf-c-dropdown__menu--Top);\n z-index: var(--pf-c-dropdown__menu--ZIndex);\n min-width: 100%;\n padding-top: var(--pf-c-dropdown__menu--PaddingTop);\n padding-bottom: var(--pf-c-dropdown__menu--PaddingBottom);\n background: var(--pf-c-dropdown__menu--BackgroundColor);\n background-clip: padding-box;\n box-shadow: var(--pf-c-dropdown__menu--BoxShadow); }\n .pf-c-dropdown__menu.pf-m-align-right {\n right: 0; }\n .pf-c-dropdown.pf-m-top .pf-c-dropdown__menu {\n --pf-c-dropdown__menu--Top: var(--pf-c-dropdown--m-top__menu--Top);\n transform: translateY(var(--pf-c-dropdown--m-top__menu--TranslateY)); }\n\n.pf-c-dropdown__menu-item {\n display: block;\n width: 100%;\n padding: var(--pf-c-dropdown__menu-item--PaddingTop) var(--pf-c-dropdown__menu-item--PaddingRight) var(--pf-c-dropdown__menu-item--PaddingBottom) var(--pf-c-dropdown__menu-item--PaddingLeft);\n font-size: var(--pf-c-dropdown__menu-item--FontSize);\n font-weight: var(--pf-c-dropdown__menu-item--FontWeight);\n line-height: var(--pf-c-dropdown__menu-item--LineHeight);\n color: var(--pf-c-dropdown__menu-item--Color);\n text-align: left;\n white-space: nowrap;\n background-color: var(--pf-c-dropdown__menu-item--BackgroundColor);\n border: none; }\n .pf-c-dropdown__menu-item:hover, .pf-c-dropdown__menu-item:focus {\n --pf-c-dropdown__menu-item--Color: var(--pf-c-dropdown__menu-item--hover--Color);\n --pf-c-dropdown__menu-item--BackgroundColor: var(--pf-c-dropdown__menu-item--hover--BackgroundColor);\n text-decoration: none; }\n .pf-c-dropdown__menu-item:disabled, .pf-c-dropdown__menu-item.pf-m-disabled {\n --pf-c-dropdown__menu-item--Color: var(--pf-c-dropdown__menu-item--disabled--Color);\n --pf-c-dropdown__menu-item--BackgroundColor: var(--pf-c-dropdown__menu-item--disabled--BackgroundColor);\n pointer-events: none; }\n .pf-c-dropdown__menu-item.pf-m-icon {\n display: flex;\n align-items: center; }\n .pf-c-dropdown__menu-item.pf-m-icon.pf-m-description {\n flex-direction: column;\n align-items: start; }\n .pf-c-dropdown__menu-item.pf-m-icon .pf-c-dropdown__menu-item-main {\n display: flex;\n align-items: center; }\n .pf-c-dropdown__menu-item.pf-m-text {\n --pf-c-dropdown__menu-item--Color: var(--pf-c-dropdown__menu-item--m-text--Color); }\n .pf-c-dropdown__menu-item.pf-m-text:hover, .pf-c-dropdown__menu-item.pf-m-text:focus {\n --pf-c-dropdown__menu-item--BackgroundColor: transparent; }\n\n.pf-c-dropdown__menu-item-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--pf-c-dropdown__menu-item-icon--Width);\n height: var(--pf-c-dropdown__menu-item-icon--Height);\n margin-right: var(--pf-c-dropdown__menu-item-icon--MarginRight); }\n .pf-c-dropdown__menu-item-icon > * {\n max-width: 100%;\n max-height: 100%; }\n\n.pf-c-dropdown__menu-item-description {\n font-size: var(--pf-c-dropdown__menu-item-description--FontSize);\n color: var(--pf-c-dropdown__menu-item-description--Color); }\n\n.pf-c-dropdown__group + .pf-c-dropdown__group {\n padding-top: var(--pf-c-dropdown__group--group--PaddingTop); }\n\n.pf-c-dropdown__group-title {\n padding-top: var(--pf-c-dropdown__group-title--PaddingTop);\n padding-right: var(--pf-c-dropdown__group-title--PaddingRight);\n padding-bottom: var(--pf-c-dropdown__group-title--PaddingBottom);\n padding-left: var(--pf-c-dropdown__group-title--PaddingLeft);\n font-size: var(--pf-c-dropdown__group-title--FontSize);\n font-weight: var(--pf-c-dropdown__group-title--FontWeight);\n color: var(--pf-c-dropdown__group-title--Color); }\n\n.pf-c-empty-state {\n --pf-c-empty-state--PaddingTop: var(--pf-global--spacer--xl);\n --pf-c-empty-state--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-empty-state--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-empty-state--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-empty-state__content--MaxWidth: none;\n --pf-c-empty-state__icon--MarginBottom: var(--pf-global--spacer--lg);\n --pf-c-empty-state__icon--FontSize: var(--pf-global--icon--FontSize--xl);\n --pf-c-empty-state__icon--Color: var(--pf-global--icon--Color--light);\n --pf-c-empty-state__content--c-title--m-lg--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-empty-state__body--MarginTop: var(--pf-global--spacer--md);\n --pf-c-empty-state__body--Color: var(--pf-global--Color--200);\n --pf-c-empty-state__primary--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-empty-state__primary--secondary--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-empty-state__secondary--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-empty-state__secondary--MarginBottom: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-empty-state__secondary--child--MarginRight: calc(var(--pf-global--spacer--xs) / 2);\n --pf-c-empty-state__secondary--child--MarginBottom: var(--pf-global--spacer--xs);\n --pf-c-empty-state__secondary--child--MarginLeft: calc(var(--pf-global--spacer--xs) / 2);\n --pf-c-empty-state--m-xs__content--MaxWidth: 21.875rem;\n --pf-c-empty-state--m-xs__body--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-empty-state--m-xs--button--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-empty-state--m-xs--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs__icon--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs__body--MarginTop: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs__primary--MarginTop: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-xs__secondary--MarginTop: var(--pf-global--spacer--md);\n --pf-c-empty-state--m-sm__content--MaxWidth: 25rem;\n --pf-c-empty-state--m-lg__content--MaxWidth: 37.5rem;\n --pf-c-empty-state--m-xl__body--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-empty-state--m-xl__body--MarginTop: var(--pf-global--spacer--lg);\n --pf-c-empty-state--m-xl__icon--MarginBottom: var(--pf-global--spacer--xl);\n --pf-c-empty-state--m-xl__icon--FontSize: 6.25rem;\n --pf-c-empty-state--m-xl--c-button__secondary--MarginTop: var(--pf-global--spacer--md);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--pf-c-empty-state--PaddingTop) var(--pf-c-empty-state--PaddingRight) var(--pf-c-empty-state--PaddingBottom) var(--pf-c-empty-state--PaddingLeft);\n text-align: center; }\n .pf-c-empty-state.pf-m-xs {\n --pf-c-empty-state--PaddingTop: var(--pf-c-empty-state--m-xs--PaddingTop);\n --pf-c-empty-state--PaddingRight: var(--pf-c-empty-state--m-xs--PaddingRight);\n --pf-c-empty-state--PaddingBottom: var(--pf-c-empty-state--m-xs--PaddingBottom);\n --pf-c-empty-state--PaddingLeft: var(--pf-c-empty-state--m-xs--PaddingLeft);\n --pf-c-empty-state__content--MaxWidth: var(--pf-c-empty-state--m-xs__content--MaxWidth);\n --pf-c-empty-state__icon--MarginBottom: var(--pf-c-empty-state--m-xs__icon--MarginBottom);\n --pf-c-empty-state__body--MarginTop: var(--pf-c-empty-state--m-xs__body--MarginTop);\n --pf-c-empty-state__primary--MarginTop: var(--pf-c-empty-state--m-xs__primary--MarginTop);\n --pf-c-empty-state__secondary--MarginTop: var(--pf-c-empty-state--m-xs__secondary--MarginTop); }\n .pf-c-empty-state.pf-m-xs .pf-c-empty-state__body {\n font-size: var(--pf-c-empty-state--m-xs__body--FontSize); }\n .pf-c-empty-state.pf-m-xs .pf-c-button {\n --pf-c-button--FontSize: var(--pf-c-empty-state--m-xs--button--FontSize); }\n .pf-c-empty-state.pf-m-sm {\n --pf-c-empty-state__content--MaxWidth: var(--pf-c-empty-state--m-sm__content--MaxWidth); }\n .pf-c-empty-state.pf-m-lg {\n --pf-c-empty-state__content--MaxWidth: var(--pf-c-empty-state--m-lg__content--MaxWidth); }\n .pf-c-empty-state.pf-m-xl {\n --pf-c-empty-state__body--MarginTop: var(--pf-c-empty-state--m-xl__body--MarginTop);\n --pf-c-empty-state__icon--MarginBottom: var(--pf-c-empty-state--m-xl__icon--MarginBottom);\n --pf-c-empty-state__icon--FontSize: var(--pf-c-empty-state--m-xl__icon--FontSize);\n --pf-c-empty-state--c-button__secondary--MarginTop: var(--pf-c-empty-state--m-xl--c-button__secondary--MarginTop); }\n .pf-c-empty-state.pf-m-xl .pf-c-empty-state__body {\n font-size: var(--pf-c-empty-state--m-xl__body--FontSize); }\n .pf-c-empty-state.pf-m-full-height {\n height: 100%; }\n\n.pf-c-empty-state__content {\n max-width: var(--pf-c-empty-state__content--MaxWidth); }\n .pf-c-empty-state__content > .pf-c-title.pf-m-lg {\n font-size: var(--pf-c-empty-state__content--c-title--m-lg--FontSize); }\n\n.pf-c-empty-state__icon {\n margin-bottom: var(--pf-c-empty-state__icon--MarginBottom);\n font-size: var(--pf-c-empty-state__icon--FontSize);\n color: var(--pf-c-empty-state__icon--Color); }\n\n.pf-c-empty-state__body {\n margin-top: var(--pf-c-empty-state__body--MarginTop);\n color: var(--pf-c-empty-state__body--Color); }\n\n.pf-c-empty-state__content > .pf-c-button.pf-m-primary,\n.pf-c-empty-state__primary {\n margin-top: var(--pf-c-empty-state__primary--MarginTop); }\n .pf-c-empty-state__content > .pf-c-button.pf-m-primary + .pf-c-empty-state__secondary,\n .pf-c-empty-state__primary + .pf-c-empty-state__secondary {\n margin-top: var(--pf-c-empty-state__primary--secondary--MarginTop); }\n\n.pf-c-empty-state__secondary {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n margin-top: var(--pf-c-empty-state__secondary--MarginTop);\n margin-bottom: var(--pf-c-empty-state__secondary--MarginBottom); }\n .pf-c-empty-state__secondary > * {\n margin-right: var(--pf-c-empty-state__secondary--child--MarginRight);\n margin-bottom: var(--pf-c-empty-state__secondary--child--MarginBottom);\n margin-left: var(--pf-c-empty-state__secondary--child--MarginLeft); }\n\n.pf-m-overpass-font .pf-c-empty-state .pf-c-empty-state__content > .pf-c-title.pf-m-lg {\n font-size: var(--pf-global--FontSize--lg); }\n\n.pf-c-expandable-section {\n --pf-c-expandable-section__toggle--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-expandable-section__toggle--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-expandable-section__toggle--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-expandable-section__toggle--PaddingLeft: 0;\n --pf-c-expandable-section__toggle--Color: var(--pf-global--link--Color);\n --pf-c-expandable-section__toggle--hover--Color: var(--pf-global--link--Color--hover);\n --pf-c-expandable-section__toggle--active--Color: var(--pf-global--link--Color--hover);\n --pf-c-expandable-section__toggle--focus--Color: var(--pf-global--link--Color--hover);\n --pf-c-expandable-section__toggle--m-expanded--Color: var(--pf-global--link--Color--hover);\n --pf-c-expandable-section__toggle-icon--Color: var(--pf-global--Color--100);\n --pf-c-expandable-section__toggle-icon--Transition: .2s ease-in 0s;\n --pf-c-expandable-section__toggle-icon--Rotate: 0;\n --pf-c-expandable-section--m-expanded__toggle-icon--Rotate: 90deg;\n --pf-c-expandable-section__toggle-text--MarginLeft: calc(var(--pf-global--spacer--xs) + var(--pf-global--spacer--sm));\n --pf-c-expandable-section__content--MarginTop: var(--pf-global--spacer--md); }\n .pf-c-expandable-section.pf-m-expanded {\n --pf-c-expandable-section__toggle--Color: var(--pf-c-expandable-section__toggle--m-expanded--Color);\n --pf-c-expandable-section__toggle-icon--Rotate: var(--pf-c-expandable-section--m-expanded__toggle-icon--Rotate); }\n\n.pf-c-expandable-section__toggle {\n display: flex;\n padding: var(--pf-c-expandable-section__toggle--PaddingTop) var(--pf-c-expandable-section__toggle--PaddingRight) var(--pf-c-expandable-section__toggle--PaddingBottom) var(--pf-c-expandable-section__toggle--PaddingLeft);\n color: var(--pf-c-expandable-section__toggle--Color);\n border: none; }\n .pf-c-expandable-section__toggle:hover {\n --pf-c-expandable-section__toggle--Color: var(--pf-c-expandable-section__toggle--hover--Color); }\n .pf-c-expandable-section__toggle:active, .pf-c-expandable-section__toggle.pf-m-active {\n --pf-c-expandable-section__toggle--Color: var(--pf-c-expandable-section__toggle--active--Color); }\n .pf-c-expandable-section__toggle:focus {\n --pf-c-expandable-section__toggle--Color: var(--pf-c-expandable-section__toggle--focus--Color); }\n\n.pf-c-expandable-section__toggle-icon {\n color: var(--pf-c-expandable-section__toggle-icon--Color);\n transition: var(--pf-c-expandable-section__toggle-icon--Transition);\n transform: rotate(var(--pf-c-expandable-section__toggle-icon--Rotate)); }\n\n.pf-c-expandable-section__toggle-text {\n margin-left: var(--pf-c-expandable-section__toggle-text--MarginLeft); }\n\n.pf-c-expandable-section__content {\n margin-top: var(--pf-c-expandable-section__content--MarginTop); }\n\n.pf-m-overpass-font .pf-c-expandable-section__toggle {\n font-weight: var(--pf-global--FontWeight--semi-bold); }\n\n.pf-c-file-upload {\n --pf-c-file-upload--m-loading__file-details--before--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-file-upload--m-loading__file-details--before--Left: var(--pf-global--BorderWidth--sm);\n --pf-c-file-upload--m-loading__file-details--before--Right: var(--pf-global--BorderWidth--sm);\n --pf-c-file-upload--m-loading__file-details--before--Bottom: var(--pf-global--BorderWidth--sm);\n --pf-c-file-upload--m-drag-hover--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-file-upload--m-drag-hover--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-file-upload--m-drag-hover--before--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-file-upload--m-drag-hover--after--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-file-upload--m-drag-hover--after--Opacity: .1;\n --pf-c-file-upload__file-details__c-form-control--MinHeight: calc(var(--pf-global--spacer--3xl) * 2);\n --pf-c-file-upload__file-select__c-button--m-control--OutlineOffset: calc(-1 * var(--pf-global--spacer--xs));\n position: relative;\n display: flex;\n flex-direction: column; }\n .pf-c-file-upload.pf-m-drag-hover::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: var(--pf-c-file-upload--m-drag-hover--before--ZIndex);\n content: \"\";\n border: var(--pf-c-file-upload--m-drag-hover--before--BorderWidth) solid var(--pf-c-file-upload--m-drag-hover--before--BorderColor); }\n .pf-c-file-upload.pf-m-drag-hover::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n background-color: var(--pf-c-file-upload--m-drag-hover--after--BackgroundColor);\n opacity: var(--pf-c-file-upload--m-drag-hover--after--Opacity); }\n .pf-c-file-upload.pf-m-loading .pf-c-file-upload__file-details {\n position: relative; }\n .pf-c-file-upload.pf-m-loading .pf-c-file-upload__file-details::before {\n position: absolute;\n top: 0;\n right: var(--pf-c-file-upload--m-loading__file-details--before--Left);\n bottom: var(--pf-c-file-upload--m-loading__file-details--before--Left);\n left: var(--pf-c-file-upload--m-loading__file-details--before--Left);\n content: \"\";\n background-color: var(--pf-c-file-upload--m-loading__file-details--before--BackgroundColor); }\n\n.pf-c-file-upload__file-select .pf-c-button.pf-m-control {\n outline-offset: var(--pf-c-file-upload__file-select__c-button--m-control--OutlineOffset); }\n\n.pf-c-file-upload__file-details {\n position: relative;\n display: flex; }\n .pf-c-file-upload__file-details .pf-c-form-control {\n flex: 1 1 auto;\n min-height: var(--pf-c-file-upload__file-details__c-form-control--MinHeight);\n border-top: 0; }\n\n.pf-c-file-upload__file-details-spinner {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%); }\n\n.pf-c-form {\n --pf-c-form--GridGap: var(--pf-global--gutter--md);\n --pf-c-form__group--m-action--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-form--m-horizontal__group-label--md--GridColumnWidth: 9.375rem;\n --pf-c-form--m-horizontal__group-label--md--GridColumnGap: var(--pf-global--spacer--md);\n --pf-c-form--m-horizontal__group-control--md--GridColumnWidth: 1fr;\n --pf-c-form--m-limit-width--MaxWidth: 31.25rem;\n --pf-c-form--m-horizontal__group-label--md--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-form__group-label--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-form__label--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-form__label--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-form__label--m-disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-form__label-text--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-form__label-required--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-form__label-required--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-form__label-required--Color: var(--pf-global--danger-color--100);\n --pf-c-form__group-label-help--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-form__group-label-help--PaddingRight: var(--pf-global--spacer--xs);\n --pf-c-form__group-label-help--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-form__group-label-help--PaddingLeft: var(--pf-global--spacer--xs);\n --pf-c-form__group-label-help--MarginTop: calc(var(--pf-c-form__group-label-help--PaddingTop) * -1);\n --pf-c-form__group-label-help--MarginRight: calc(var(--pf-c-form__group-label-help--PaddingRight) * -1);\n --pf-c-form__group-label-help--MarginBottom: calc(var(--pf-c-form__group-label-help--PaddingBottom) * -1);\n --pf-c-form__group-label-help--MarginLeft: calc(var(--pf-c-form__group-label-help--PaddingLeft) * -1 + var(--pf-global--spacer--xs));\n --pf-c-form__group-label-help--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-form__group-label-help--TranslateY: 0.125rem;\n --pf-c-form__group-control--m-inline--child--MarginRight: var(--pf-global--spacer--lg);\n --pf-c-form__group-control__helper-text--MarginBottom: var(--pf-global--spacer--xs);\n --pf-c-form__actions--child--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-form__actions--child--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-form__actions--child--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-form__actions--child--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-form__actions--MarginTop: calc(var(--pf-c-form__actions--child--MarginTop) * -1);\n --pf-c-form__actions--MarginRight: calc(var(--pf-c-form__actions--child--MarginRight) * -1);\n --pf-c-form__actions--MarginBottom: calc(var(--pf-c-form__actions--child--MarginBottom) * -1);\n --pf-c-form__actions--MarginLeft: calc(var(--pf-c-form__actions--child--MarginLeft) * -1);\n --pf-c-form__helper-text--MarginTop: var(--pf-global--spacer--xs);\n --pf-c-form__helper-text--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-form__helper-text--Color: var(--pf-global--Color--100);\n --pf-c-form__helper-text-icon--FontSize: var(--pf-global--FontSize--md);\n --pf-c-form__helper-text-icon--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-form__helper-text--m-success--Color: var(--pf-global--success-color--200);\n --pf-c-form__helper-text--m-warning--Color: var(--pf-global--warning-color--200);\n --pf-c-form__helper-text--m-error--Color: var(--pf-global--danger-color--100);\n --pf-c-form__section--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-form__section--Gap: var(--pf-global--gutter--md);\n --pf-c-form__field-group--border-width-base: var(--pf-global--BorderWidth--sm);\n --pf-c-form__field-group--BorderTopWidth: var(--pf-c-form__field-group--border-width-base);\n --pf-c-form__field-group--BorderTopColor: var(--pf-global--BorderColor--100);\n --pf-c-form__field-group--BorderBottomWidth: var(--pf-c-form__field-group--border-width-base);\n --pf-c-form__field-group--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-form__field-group--field-group--MarginTop: calc(var(--pf-c-form--GridGap) * -1);\n --pf-c-form__field-group--GridTemplateColumns--toggle: calc(var(--pf-global--spacer--md) * 2 + var(--pf-c-form__field-group-toggle-icon--MinWidth) + var(--pf-global--spacer--xs));\n --pf-c-form__field-group-toggle--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-form__field-group-toggle--PaddingRight: var(--pf-global--spacer--xs);\n --pf-c-form__field-group__field-group__field-group-toggle--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-form__field-group-header-toggle--BorderWidth--base: var(--pf-global--BorderWidth--sm);\n --pf-c-form__field-group__field-group--field-group__field-group-toggle--after--BorderTopWidth: var(--pf-c-form__field-group-header-toggle--BorderWidth--base);\n --pf-c-form__field-group-toggle-button--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-form__field-group-toggle-button--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-form__field-group-toggle-icon--Transition: var(--pf-global--Transition);\n --pf-c-form__field-group-toggle-icon--MinWidth: var(--pf-global--FontSize--md);\n --pf-c-form__field-group-toggle-icon--Rotate: 0;\n --pf-c-form__field-group--m-expanded__toggle-icon--Rotate: 90deg;\n --pf-c-form__field-group-header--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-form__field-group-header--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-form__field-group-header--GridColumn: 1 / 3;\n --pf-c-form__field-group__field-group__field-group-header--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-form__field-group__field-group__field-group-header--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-form__field-group-toggle--field-group-header--GridColumn: 2 / 3;\n --pf-c-form__field-group__field-group--field-group__field-group-header--after--BorderTopWidth: var(--pf-c-form__field-group-header-toggle--BorderWidth--base);\n --pf-c-form__field-group-header-description--MarginTop: var(--pf-global--spacer--xs);\n --pf-c-form__field-group-header-description--Color: var(--pf-global--Color--200);\n --pf-c-form__field-group-header-actions--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-form__field-group-header-actions--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-form__field-group-header-actions--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-form__field-group-body--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-form__field-group-body--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-form__field-group-body--Gap: var(--pf-c-form--GridGap);\n --pf-c-form__field-group-body--GridColumn: 2 / 3;\n --pf-c-form__field-group__field-group__field-group-body--GridColumn: 1 / 3;\n --pf-c-form__field-group__field-group__field-group-toggle--field-group-body--GridColumn: 2 / 3;\n --pf-c-form__field-group__field-group--not--m-expandable__field-group--not-m-expandable__field-group-header--GridColumn: 2 / 3;\n --pf-c-form__field-group__field-group--not--m-expandable__field-group--not-m-expandable__field-group-body--GridColumn: 2 / 3;\n --pf-c-form__field-group-body__field-group--last-child--MarginBottom: calc(var(--pf-c-form__field-group-body--PaddingBottom) * -1);\n display: grid;\n grid-gap: var(--pf-c-form--GridGap); }\n .pf-c-form.pf-m-horizontal {\n --pf-c-form__group-label--PaddingBottom: 0; }\n .pf-c-form.pf-m-horizontal.pf-m-align-right .pf-c-form__label {\n text-align: right; }\n @media (min-width: 768px) {\n .pf-c-form.pf-m-horizontal .pf-c-form__group {\n display: grid;\n grid-column-gap: var(--pf-c-form--m-horizontal__group-label--md--GridColumnGap);\n grid-template-columns: var(--pf-c-form--m-horizontal__group-label--md--GridColumnWidth) var(--pf-c-form--m-horizontal__group-control--md--GridColumnWidth); }\n .pf-c-form.pf-m-horizontal .pf-c-form__group-label {\n padding-top: var(--pf-c-form--m-horizontal__group-label--md--PaddingTop); }\n .pf-c-form.pf-m-horizontal .pf-c-form__group-label.pf-m-no-padding-top {\n --pf-c-form--m-horizontal__group-label--md--PaddingTop: 0; }\n .pf-c-form.pf-m-horizontal .pf-c-form__group-control {\n grid-column: 2; } }\n .pf-c-form.pf-m-limit-width {\n max-width: var(--pf-c-form--m-limit-width--MaxWidth); }\n\n.pf-c-form__group.pf-m-action {\n margin-top: var(--pf-c-form__group--m-action--MarginTop);\n overflow: hidden; }\n\n.pf-c-form__section {\n display: grid;\n gap: var(--pf-c-form__section--Gap); }\n .pf-c-form__section + .pf-c-form__group:not(.pf-m-action), .pf-c-form__section:not(:first-child) {\n margin-top: var(--pf-c-form__section--MarginTop); }\n\n.pf-c-form__group-label {\n --pf-c-form__helper-text--MarginTop: 0;\n padding-bottom: var(--pf-c-form__group-label--PaddingBottom); }\n\n.pf-c-form__label {\n font-size: var(--pf-c-form__label--FontSize);\n line-height: var(--pf-c-form__label--LineHeight); }\n .pf-c-form__label::selection {\n background-color: none; }\n .pf-c-form__label:not(.pf-m-disabled):hover {\n cursor: pointer; }\n .pf-c-form__label.pf-m-disabled {\n color: var(--pf-c-form__label--m-disabled--Color); }\n .pf-c-form__label.pf-m-disabled:hover {\n cursor: not-allowed; }\n\n.pf-c-form__label-text {\n font-weight: var(--pf-c-form__label-text--FontWeight); }\n\n.pf-c-form__label-required {\n margin-left: var(--pf-c-form__label-required--MarginLeft);\n font-size: var(--pf-c-form__label-required--FontSize);\n color: var(--pf-c-form__label-required--Color); }\n\n.pf-c-form__group-label-help {\n padding-top: var(--pf-c-form__group-label-help--PaddingTop);\n padding-right: var(--pf-c-form__group-label-help--PaddingRight);\n padding-bottom: var(--pf-c-form__group-label-help--PaddingBottom);\n padding-left: var(--pf-c-form__group-label-help--PaddingLeft);\n margin-top: var(--pf-c-form__group-label-help--MarginTop);\n margin-right: var(--pf-c-form__group-label-help--MarginRight);\n margin-bottom: var(--pf-c-form__group-label-help--MarginBottom);\n margin-left: var(--pf-c-form__group-label-help--MarginLeft);\n font-size: var(--pf-c-form__group-label-help--FontSize);\n line-height: 1;\n border: 0;\n transform: translateY(var(--pf-c-form__group-label-help--TranslateY)); }\n\n.pf-c-form__group-control.pf-m-inline {\n display: flex;\n flex-flow: row wrap; }\n .pf-c-form__group-control.pf-m-inline > * {\n margin-right: var(--pf-c-form__group-control--m-inline--child--MarginRight); }\n\n.pf-c-form__group-control .pf-c-form__helper-text:first-child {\n --pf-c-form__helper-text--MarginTop: 0;\n margin-bottom: var(--pf-c-form__group-control__helper-text--MarginBottom); }\n\n.pf-c-form__helper-text {\n margin-top: var(--pf-c-form__helper-text--MarginTop);\n font-size: var(--pf-c-form__helper-text--FontSize);\n color: var(--pf-c-form__helper-text--Color); }\n .pf-c-form__helper-text.pf-m-error {\n --pf-c-form__helper-text--Color: var(--pf-c-form__helper-text--m-error--Color); }\n .pf-c-form__helper-text.pf-m-success {\n --pf-c-form__helper-text--Color: var(--pf-c-form__helper-text--m-success--Color); }\n .pf-c-form__helper-text.pf-m-warning {\n --pf-c-form__helper-text--Color: var(--pf-c-form__helper-text--m-warning--Color); }\n .pf-c-form__helper-text.pf-m-inactive {\n display: none;\n visibility: hidden; }\n .pf-c-form__helper-text.pf-m-hidden {\n visibility: hidden;\n opacity: 0; }\n\n.pf-c-form__helper-text-icon {\n margin-right: var(--pf-c-form__helper-text-icon--MarginRight);\n font-size: var(--pf-c-form__helper-text-icon--FontSize); }\n\n.pf-c-form__fieldset {\n border: 0; }\n\n.pf-c-form__actions {\n display: flex;\n flex-wrap: wrap;\n margin-top: var(--pf-c-form__actions--MarginTop);\n margin-right: var(--pf-c-form__actions--MarginRight);\n margin-bottom: var(--pf-c-form__actions--MarginBottom);\n margin-left: var(--pf-c-form__actions--MarginLeft); }\n .pf-c-form__actions > * {\n margin-top: var(--pf-c-form__actions--child--MarginTop);\n margin-right: var(--pf-c-form__actions--child--MarginRight);\n margin-bottom: var(--pf-c-form__actions--child--MarginBottom);\n margin-left: var(--pf-c-form__actions--child--MarginLeft); }\n\n.pf-c-form__field-group {\n --pf-c-form__field-group--BorderTopWidth: var(--pf-c-form__field-group--border-width-base);\n --pf-c-form__field-group--BorderTopWidth: var(--pf-c-form__field-group--border-width-base);\n display: grid;\n grid-template-columns: minmax(var(--pf-c-form__field-group--GridTemplateColumns--toggle), max-content) 1fr;\n border-top: var(--pf-c-form__field-group--BorderTopWidth) solid var(--pf-c-form__field-group--BorderTopColor);\n border-bottom: var(--pf-c-form__field-group--BorderBottomWidth) solid var(--pf-c-form__field-group--BorderBottomColor); }\n .pf-c-form__field-group:last-child {\n --pf-c-form__field-group--BorderBottomWidth: 0; }\n .pf-c-form__field-group + .pf-c-form__field-group, .pf-c-form__field-group:first-child {\n --pf-c-form__field-group--BorderTopWidth: 0; }\n .pf-c-form__field-group + .pf-c-form__field-group {\n margin-top: var(--pf-c-form__field-group--field-group--MarginTop); }\n .pf-c-form__field-group .pf-c-form__field-group {\n --pf-c-form__field-group-body--GridColumn: var(--pf-c-form__field-group__field-group__field-group-body--GridColumn);\n --pf-c-form__field-group-toggle--PaddingTop: var(--pf-c-form__field-group__field-group__field-group-toggle--PaddingTop);\n --pf-c-form__field-group-header--PaddingTop: var(--pf-c-form__field-group__field-group__field-group-header--PaddingTop);\n --pf-c-form__field-group-header--PaddingBottom: var(--pf-c-form__field-group__field-group__field-group-header--PaddingBottom);\n --pf-c-form__field-group-body--PaddingTop: 0; }\n .pf-c-form__field-group .pf-c-form__field-group .pf-c-form__field-group-toggle ~ .pf-c-form__field-group-body {\n --pf-c-form__field-group-body--GridColumn: var(--pf-c-form__field-group__field-group__field-group-toggle--field-group-body--GridColumn); }\n .pf-c-form__field-group.pf-m-expanded > .pf-c-form__field-group-toggle {\n --pf-c-form__field-group-toggle-icon--Rotate: var(--pf-c-form__field-group--m-expanded__toggle-icon--Rotate); }\n\n.pf-c-form__field-group-toggle {\n grid-column: 1 / 2;\n grid-row: 1 / 2;\n padding-top: var(--pf-c-form__field-group-toggle--PaddingTop);\n padding-right: var(--pf-c-form__field-group-toggle--PaddingRight); }\n .pf-c-form__field-group-toggle + .pf-c-form__field-group-header {\n --pf-c-form__field-group-header--GridColumn: var(--pf-c-form__field-group-toggle--field-group-header--GridColumn); }\n\n.pf-c-form__field-group-toggle-button {\n margin-top: var(--pf-c-form__field-group-toggle-button--MarginTop);\n margin-bottom: var(--pf-c-form__field-group-toggle-button--MarginBottom); }\n\n.pf-c-form__field-group-toggle-icon {\n display: inline-block;\n min-width: var(--pf-c-form__field-group-toggle-icon--MinWidth);\n text-align: center;\n transition: var(--pf-c-form__field-group-toggle-icon--Transition);\n transform: rotate(var(--pf-c-form__field-group-toggle-icon--Rotate)); }\n\n.pf-c-form__field-group-header {\n grid-column: var(--pf-c-form__field-group-header--GridColumn);\n grid-row: 1 / 2;\n display: flex;\n align-items: flex-start;\n padding-top: var(--pf-c-form__field-group-header--PaddingTop);\n padding-bottom: var(--pf-c-form__field-group-header--PaddingBottom); }\n\n.pf-c-form__field-group-header-main {\n display: flex;\n flex-direction: column;\n flex-grow: 1; }\n\n.pf-c-form__field-group-header-title {\n display: flex; }\n\n.pf-c-form__field-group-header-title-text {\n flex-grow: 1; }\n\n.pf-c-form__field-group-header-description {\n margin-top: var(--pf-c-form__field-group-header-description--MarginTop);\n color: var(--pf-c-form__field-group-header-description--Color); }\n\n.pf-c-form__field-group-header-actions {\n margin-top: var(--pf-c-form__field-group-header-actions--MarginTop);\n margin-bottom: var(--pf-c-form__field-group-header-actions--MarginBottom);\n margin-left: var(--pf-c-form__field-group-header-actions--MarginLeft);\n white-space: nowrap; }\n\n.pf-c-form__field-group-body {\n grid-column: var(--pf-c-form__field-group-body--GridColumn);\n display: grid;\n gap: var(--pf-c-form__field-group-body--Gap);\n padding-top: var(--pf-c-form__field-group-body--PaddingTop);\n padding-bottom: var(--pf-c-form__field-group-body--PaddingBottom); }\n .pf-c-form__field-group-body > .pf-c-form__field-group:first-child {\n --pf-c-form__field-group-toggle--PaddingTop: 0;\n --pf-c-form__field-group-header--PaddingTop: 0; }\n .pf-c-form__field-group-body > .pf-c-form__field-group:last-child {\n margin-bottom: var(--pf-c-form__field-group-body__field-group--last-child--MarginBottom); }\n\n.pf-c-form-control {\n --pf-c-form-control--FontSize: var(--pf-global--FontSize--md);\n --pf-c-form-control--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-form-control--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-form-control--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-form-control--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-form-control--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-form-control--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-form-control--BorderRadius: 0;\n --pf-c-form-control--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-form-control--Height: calc(var(--pf-c-form-control--FontSize) * var(--pf-c-form-control--LineHeight) + var(--pf-c-form-control--BorderWidth) * 2 + var(--pf-c-form-control--PaddingTop) + var(--pf-c-form-control--PaddingBottom));\n --pf-c-form-control--inset--base: var(--pf-global--spacer--sm);\n --pf-c-form-control--PaddingTop: calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));\n --pf-c-form-control--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--inset--base);\n --pf-c-form-control--PaddingLeft: var(--pf-c-form-control--inset--base);\n --pf-c-form-control--hover--BorderBottomColor: var(--pf-global--primary-color--100);\n --pf-c-form-control--focus--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-form-control--focus--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--focus--BorderBottomWidth));\n --pf-c-form-control--focus--BorderBottomColor: var(--pf-global--primary-color--100);\n --pf-c-form-control--m-expanded--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-form-control--m-expanded--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--focus--BorderBottomWidth));\n --pf-c-form-control--m-expanded--BorderBottomColor: var(--pf-global--primary-color--100);\n --pf-c-form-control--placeholder--Color: var(--pf-global--Color--dark-200);\n --pf-c-form-control--disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-form-control--disabled--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-form-control--disabled--BorderColor: transparent;\n --pf-c-form-control--readonly--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-form-control--readonly--hover--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-form-control--readonly--focus--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));\n --pf-c-form-control--readonly--focus--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-form-control--readonly--focus--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-form-control--success--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-form-control--success--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--success--BorderBottomWidth));\n --pf-c-form-control--success--BorderBottomColor: var(--pf-global--success-color--100);\n --pf-c-form-control--success--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-form-control--success--BackgroundPositionX: calc(100% - var(--pf-c-form-control--PaddingLeft));\n --pf-c-form-control--success--BackgroundPositionY: center;\n --pf-c-form-control--success--BackgroundPosition: var(--pf-c-form-control--success--BackgroundPositionX) var(--pf-c-form-control--success--BackgroundPositionY);\n --pf-c-form-control--success--BackgroundSizeX: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--success--BackgroundSizeY: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--success--BackgroundSize: var(--pf-c-form-control--success--BackgroundSizeX) var(--pf-c-form-control--success--BackgroundSizeY);\n --pf-c-form-control--success--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%233e8635' d='M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z'/%3E%3C/svg%3E\");\n --pf-c-form-control--m-warning--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-form-control--m-warning--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--m-warning--BorderBottomWidth));\n --pf-c-form-control--m-warning--BorderBottomColor: var(--pf-global--warning-color--100);\n --pf-c-form-control--m-warning--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-form-control--m-warning--BackgroundPositionX: calc(100% - calc(var(--pf-c-form-control--PaddingLeft) - 0.0625rem));\n --pf-c-form-control--m-warning--BackgroundPositionY: center;\n --pf-c-form-control--m-warning--BackgroundPosition: var(--pf-c-form-control--m-warning--BackgroundPositionX) var(--pf-c-form-control--m-warning--BackgroundPositionY);\n --pf-c-form-control--m-warning--BackgroundSizeX: 1.25rem;\n --pf-c-form-control--m-warning--BackgroundSizeY: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--m-warning--BackgroundSize: var(--pf-c-form-control--m-warning--BackgroundSizeX) var(--pf-c-form-control--m-warning--BackgroundSizeY);\n --pf-c-form-control--m-warning--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23f0ab00' d='M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z'/%3E%3C/svg%3E\");\n --pf-c-form-control--invalid--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-form-control--invalid--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-c-form-control--invalid--BorderBottomWidth));\n --pf-c-form-control--invalid--BorderBottomColor: var(--pf-global--danger-color--100);\n --pf-c-form-control--invalid--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-form-control--invalid--BackgroundPositionX: calc(100% - var(--pf-c-form-control--PaddingLeft));\n --pf-c-form-control--invalid--BackgroundPositionY: center;\n --pf-c-form-control--invalid--BackgroundPosition: var(--pf-c-form-control--invalid--BackgroundPositionX) var(--pf-c-form-control--invalid--BackgroundPositionY);\n --pf-c-form-control--invalid--BackgroundSizeX: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--invalid--BackgroundSizeY: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--invalid--BackgroundSize: var(--pf-c-form-control--invalid--BackgroundSizeX) var(--pf-c-form-control--invalid--BackgroundSizeY);\n --pf-c-form-control--invalid--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23c9190b' d='M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z'/%3E%3C/svg%3E\");\n --pf-c-form-control--invalid--exclamation--Background: var(--pf-c-form-control--invalid--BackgroundUrl) var(--pf-c-form-control--invalid--BackgroundPosition) / var(--pf-c-form-control--invalid--BackgroundSize) no-repeat;\n --pf-c-form-control--invalid--Background: var(--pf-c-form-control--BackgroundColor) var(--pf-c-form-control--invalid--exclamation--Background);\n --pf-c-form-control--m-search--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-form-control--m-search--BackgroundPosition: var(--pf-c-form-control--PaddingRight);\n --pf-c-form-control--m-search--BackgroundSize: var(--pf-c-form-control--FontSize) var(--pf-c-form-control--FontSize);\n --pf-c-form-control--m-search--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%236a6e73' d='M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z'/%3E%3C/svg%3E\");\n --pf-c-form-control--m-icon--PaddingRight: calc(var(--pf-c-form-control--inset--base) + var(--pf-c-form-control--m-icon--BackgroundSizeX) + var(--pf-c-form-control--m-icon--icon--spacer));\n --pf-c-form-control--m-icon--BackgroundUrl: none;\n --pf-c-form-control--m-icon--BackgroundPositionX: calc(100% - var(--pf-c-form-control--inset--base));\n --pf-c-form-control--m-icon--BackgroundPositionY: center;\n --pf-c-form-control--m-icon--BackgroundSizeX: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--m-icon--BackgroundSizeY: var(--pf-c-form-control--FontSize);\n --pf-c-form-control--m-icon--icon--spacer: var(--pf-global--spacer--sm);\n --pf-c-form-control--m-icon--icon--PaddingRight: calc(var(--pf-c-form-control--inset--base) + var(--pf-c-form-control--invalid--BackgroundSizeX) + var(--pf-c-form-control--m-icon--icon--spacer) + var(--pf-c-form-control--m-icon--BackgroundSizeX) + var(--pf-c-form-control--m-icon--icon--spacer));\n --pf-c-form-control--m-icon--icon--BackgroundPositionX: calc(var(--pf-c-form-control--m-icon--BackgroundPositionX) - var(--pf-c-form-control--m-icon--icon--spacer) - var(--pf-c-form-control--invalid--BackgroundSizeX));\n --pf-c-form-control--m-icon--invalid--BackgroundUrl: var(--pf-c-form-control--invalid--BackgroundUrl), var(--pf-c-form-control--m-icon--BackgroundUrl);\n --pf-c-form-control--m-icon--invalid--BackgroundPosition: var(--pf-c-form-control--invalid--BackgroundPosition), var(--pf-c-form-control--m-icon--icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);\n --pf-c-form-control--m-icon--invalid--BackgroundSize: var(--pf-c-form-control--invalid--BackgroundSize), var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY);\n --pf-c-form-control--m-icon--success--BackgroundUrl: var(--pf-c-form-control--success--BackgroundUrl), var(--pf-c-form-control--m-icon--BackgroundUrl);\n --pf-c-form-control--m-icon--success--BackgroundPosition: var(--pf-c-form-control--success--BackgroundPosition), var(--pf-c-form-control--m-icon--icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);\n --pf-c-form-control--m-icon--success--BackgroundSize: var(--pf-c-form-control--success--BackgroundSize), var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY);\n --pf-c-form-control--m-icon--m-warning--BackgroundUrl: var(--pf-c-form-control--m-warning--BackgroundUrl), var(--pf-c-form-control--m-icon--BackgroundUrl);\n --pf-c-form-control--m-icon--m-warning--BackgroundPosition: var(--pf-c-form-control--m-warning--BackgroundPosition), var(--pf-c-form-control--m-icon--icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);\n --pf-c-form-control--m-icon--m-warning--BackgroundSize: var(--pf-c-form-control--m-warning--BackgroundSize), var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY);\n --pf-c-form-control--m-calendar--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%236a6e73' d='M0 464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V192H0v272zm320-196c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zM192 268c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-40c-6.6 0-12-5.4-12-12v-40zM64 268c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12v-40zm0 128c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H76c-6.6 0-12-5.4-12-12v-40zM400 64h-48V16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v48H160V16c0-8.8-7.2-16-16-16h-32c-8.8 0-16 7.2-16 16v48H48C21.5 64 0 85.5 0 112v48h448v-48c0-26.5-21.5-48-48-48z'/%3E%3C/svg%3E\");\n --pf-c-form-control--m-clock--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%236a6e73' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm61.8-104.4l-84.9-61.7c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h32c6.6 0 12 5.4 12 12v141.7l66.8 48.6c5.4 3.9 6.5 11.4 2.6 16.8L334.6 349c-3.9 5.3-11.4 6.5-16.8 2.6z'/%3E%3C/svg%3E\");\n --pf-c-form-control__select--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-form-control__select--BackgroundUrl: url(\"data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='%23urrentColor' d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'/%3E%3C/svg%3E\");\n --pf-c-form-control__select--BackgroundSize: .625em;\n --pf-c-form-control__select--BackgroundPositionX: calc(100% - var(--pf-global--spacer--md) + 1px);\n --pf-c-form-control__select--BackgroundPositionY: center;\n --pf-c-form-control__select--BackgroundPosition: var(--pf-c-form-control__select--BackgroundPositionX) var(--pf-c-form-control__select--BackgroundPositionY);\n --pf-c-form-control__select--success--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-form-control__select--success--BackgroundPosition: calc(var(--pf-c-form-control__select--BackgroundPositionX) - var(--pf-global--spacer--lg));\n --pf-c-form-control__select--m-warning--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-form-control__select--m-warning--BackgroundPosition: calc(var(--pf-c-form-control__select--BackgroundPositionX) - var(--pf-global--spacer--lg) + 0.0625rem);\n --pf-c-form-control__select--invalid--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-form-control__select--invalid--BackgroundPosition: calc(var(--pf-c-form-control__select--BackgroundPositionX) - var(--pf-global--spacer--lg));\n --pf-c-form-control--textarea--success--BackgroundPositionY: var(--pf-c-form-control--PaddingLeft);\n --pf-c-form-control--textarea--m-warning--BackgroundPositionY: var(--pf-c-form-control--PaddingLeft);\n --pf-c-form-control--textarea--invalid--BackgroundPositionY: var(--pf-c-form-control--PaddingLeft);\n color: var(--pf-global--Color--100);\n width: 100%;\n padding: var(--pf-c-form-control--PaddingTop) var(--pf-c-form-control--PaddingRight) var(--pf-c-form-control--PaddingBottom) var(--pf-c-form-control--PaddingLeft);\n font-size: var(--pf-c-form-control--FontSize);\n line-height: var(--pf-c-form-control--LineHeight);\n background-color: var(--pf-c-form-control--BackgroundColor);\n background-repeat: no-repeat;\n border: var(--pf-c-form-control--BorderWidth) solid;\n border-color: var(--pf-c-form-control--BorderTopColor) var(--pf-c-form-control--BorderRightColor) var(--pf-c-form-control--BorderBottomColor) var(--pf-c-form-control--BorderLeftColor);\n border-radius: var(--pf-c-form-control--BorderRadius);\n -moz-appearance: none;\n -webkit-appearance: none; }\n .pf-c-form-control::placeholder {\n color: var(--pf-c-form-control--placeholder--Color); }\n .pf-c-form-control:not(textarea) {\n height: var(--pf-c-form-control--Height);\n text-overflow: ellipsis; }\n .pf-c-form-control[readonly] {\n background-color: var(--pf-c-form-control--readonly--BackgroundColor); }\n .pf-c-form-control[readonly]:not(.pf-m-success):not([aria-invalid=\"true\"]):hover {\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--readonly--hover--BorderBottomColor); }\n .pf-c-form-control[readonly]:not(.pf-m-success):not([aria-invalid=\"true\"]):focus {\n --pf-c-form-control--focus--PaddingBottom: var(--pf-c-form-control--readonly--focus--PaddingBottom);\n --pf-c-form-control--focus--BorderBottomWidth: var(--pf-c-form-control--readonly--focus--BorderBottomWidth);\n --pf-c-form-control--focus--BorderBottomColor: var(--pf-c-form-control--readonly--focus--BorderBottomColor); }\n .pf-c-form-control:hover {\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--hover--BorderBottomColor); }\n .pf-c-form-control:focus {\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--focus--BorderBottomColor);\n padding-bottom: var(--pf-c-form-control--focus--PaddingBottom);\n border-bottom-width: var(--pf-c-form-control--focus--BorderBottomWidth); }\n .pf-c-form-control.pf-m-expanded {\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--m-expanded--BorderBottomColor);\n padding-bottom: var(--pf-c-form-control--m-expanded--PaddingBottom);\n border-bottom-width: var(--pf-c-form-control--m-expanded--BorderBottomWidth); }\n .pf-c-form-control:disabled {\n --pf-c-form-control--Color: var(--pf-c-form-control--disabled--Color);\n --pf-c-form-control--BackgroundColor: var(--pf-c-form-control--disabled--BackgroundColor);\n cursor: not-allowed;\n border-color: var(--pf-c-form-control--disabled--BorderColor); }\n .pf-c-form-control[aria-invalid=\"true\"] {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--invalid--PaddingRight);\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--invalid--BorderBottomColor);\n padding-bottom: var(--pf-c-form-control--invalid--PaddingBottom);\n background-image: var(--pf-c-form-control--invalid--BackgroundUrl);\n background-position: var(--pf-c-form-control--invalid--BackgroundPosition);\n background-size: var(--pf-c-form-control--invalid--BackgroundSize);\n border-bottom-width: var(--pf-c-form-control--invalid--BorderBottomWidth); }\n .pf-c-form-control[aria-invalid=\"true\"].pf-m-icon {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--m-icon--icon--PaddingRight);\n background-image: var(--pf-c-form-control--m-icon--invalid--BackgroundUrl);\n background-position: var(--pf-c-form-control--m-icon--invalid--BackgroundPosition);\n background-size: var(--pf-c-form-control--m-icon--invalid--BackgroundSize); }\n .pf-c-form-control.pf-m-success {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--success--PaddingRight);\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--success--BorderBottomColor);\n padding-bottom: var(--pf-c-form-control--success--PaddingBottom);\n background-image: var(--pf-c-form-control--success--BackgroundUrl);\n background-position: var(--pf-c-form-control--success--BackgroundPosition);\n background-size: var(--pf-c-form-control--success--BackgroundSize);\n border-bottom-width: var(--pf-c-form-control--success--BorderBottomWidth); }\n .pf-c-form-control.pf-m-success.pf-m-icon {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--m-icon--icon--PaddingRight);\n background-image: var(--pf-c-form-control--m-icon--success--BackgroundUrl);\n background-position: var(--pf-c-form-control--m-icon--success--BackgroundPosition);\n background-size: var(--pf-c-form-control--m-icon--success--BackgroundSize); }\n .pf-c-form-control.pf-m-warning {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--m-warning--PaddingRight);\n --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--m-warning--BorderBottomColor);\n padding-bottom: var(--pf-c-form-control--m-warning--PaddingBottom);\n background-image: var(--pf-c-form-control--m-warning--BackgroundUrl);\n background-position: var(--pf-c-form-control--m-warning--BackgroundPosition);\n background-size: var(--pf-c-form-control--m-warning--BackgroundSize);\n border-bottom-width: var(--pf-c-form-control--m-warning--BorderBottomWidth); }\n .pf-c-form-control.pf-m-warning.pf-m-icon {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--m-icon--icon--PaddingRight);\n background-image: var(--pf-c-form-control--m-icon--m-warning--BackgroundUrl);\n background-position: var(--pf-c-form-control--m-icon--m-warning--BackgroundPosition);\n background-size: var(--pf-c-form-control--m-icon--m-warning--BackgroundSize); }\n .pf-c-form-control.pf-m-search {\n --pf-c-form-control--PaddingLeft: var(--pf-c-form-control--m-search--PaddingLeft);\n background-image: var(--pf-c-form-control--m-search--BackgroundUrl);\n background-position: var(--pf-c-form-control--m-search--BackgroundPosition);\n background-size: var(--pf-c-form-control--m-search--BackgroundSize); }\n .pf-c-form-control.pf-m-icon {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control--m-icon--PaddingRight);\n background-image: var(--pf-c-form-control--m-icon--BackgroundUrl);\n background-position: var(--pf-c-form-control--m-icon--BackgroundPositionX) var(--pf-c-form-control--m-icon--BackgroundPositionY);\n background-size: var(--pf-c-form-control--m-icon--BackgroundSizeX) var(--pf-c-form-control--m-icon--BackgroundSizeY); }\n .pf-c-form-control.pf-m-icon.pf-m-calendar {\n --pf-c-form-control--m-icon--BackgroundUrl: var(--pf-c-form-control--m-calendar--BackgroundUrl); }\n .pf-c-form-control.pf-m-icon.pf-m-clock {\n --pf-c-form-control--m-icon--BackgroundUrl: var(--pf-c-form-control--m-clock--BackgroundUrl); }\n select.pf-c-form-control {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control__select--PaddingRight);\n background-image: var(--pf-c-form-control__select--BackgroundUrl);\n background-position: var(--pf-c-form-control__select--BackgroundPosition);\n background-size: var(--pf-c-form-control__select--BackgroundSize); }\n select.pf-c-form-control[aria-invalid=\"true\"] {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control__select--invalid--PaddingRight);\n --pf-c-form-control--invalid--BackgroundPosition: var(--pf-c-form-control__select--invalid--BackgroundPosition);\n background-image: var(--pf-c-form-control__select--BackgroundUrl), var(--pf-c-form-control--invalid--BackgroundUrl);\n background-position: var(--pf-c-form-control__select--BackgroundPosition), var(--pf-c-form-control--invalid--BackgroundPosition);\n background-size: var(--pf-c-form-control__select--BackgroundSize), var(--pf-c-form-control--invalid--BackgroundSize); }\n select.pf-c-form-control.pf-m-success {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control__select--success--PaddingRight);\n --pf-c-form-control--success--BackgroundPosition: var(--pf-c-form-control__select--success--BackgroundPosition);\n background-image: var(--pf-c-form-control__select--BackgroundUrl), var(--pf-c-form-control--success--BackgroundUrl);\n background-position: var(--pf-c-form-control__select--BackgroundPosition), var(--pf-c-form-control--success--BackgroundPosition);\n background-size: var(--pf-c-form-control__select--BackgroundSize), var(--pf-c-form-control--success--BackgroundSize); }\n select.pf-c-form-control.pf-m-warning {\n --pf-c-form-control--PaddingRight: var(--pf-c-form-control__select--m-warning--PaddingRight);\n background-image: var(--pf-c-form-control__select--BackgroundUrl), var(--pf-c-form-control--m-warning--BackgroundUrl);\n background-position: var(--pf-c-form-control__select--BackgroundPosition), var(--pf-c-form-control__select--m-warning--BackgroundPosition);\n background-size: var(--pf-c-form-control__select--BackgroundSize), var(--pf-c-form-control--m-warning--BackgroundSize); }\n textarea.pf-c-form-control {\n --pf-c-form-control--success--BackgroundPositionY: var(--pf-c-form-control--textarea--success--BackgroundPositionY);\n --pf-c-form-control--invalid--BackgroundPositionY: var(--pf-c-form-control--textarea--invalid--BackgroundPositionY);\n --pf-c-form-control--m-warning--BackgroundPositionY: var(--pf-c-form-control--textarea--m-warning--BackgroundPositionY); }\n .pf-c-form-control.pf-m-resize-vertical {\n resize: vertical; }\n .pf-c-form-control.pf-m-resize-horizontal {\n resize: horizontal; }\n\n.pf-c-hint {\n --pf-c-hint--GridRowGap: var(--pf-global--spacer--md);\n --pf-c-hint--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-hint--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-hint--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-hint--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-hint--BackgroundColor: var(--pf-global--palette--blue-50);\n --pf-c-hint--BorderColor: var(--pf-global--palette--blue-100);\n --pf-c-hint--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-hint--BoxShadow: var(--pf-global--BoxShadow--sm);\n --pf-c-hint--Color: var(--pf-global--Color--100);\n --pf-c-hint__title--FontSize: var(--pf-global--FontSize--lg);\n --pf-c-hint__body--FontSize: var(--pf-global--FontSize--md);\n --pf-c-hint__footer--child--MarginRight: var(--pf-global--spacer--md);\n --pf-c-hint__actions--MarginLeft: var(--pf-global--spacer--2xl);\n --pf-c-hint__actions--c-dropdown--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n display: grid;\n grid-template-columns: 1fr auto;\n grid-row-gap: var(--pf-c-hint--GridRowGap);\n padding: var(--pf-c-hint--PaddingTop) var(--pf-c-hint--PaddingRight) var(--pf-c-hint--PaddingBottom) var(--pf-c-hint--PaddingLeft);\n color: var(--pf-c-hint--Color);\n background-color: var(--pf-c-hint--BackgroundColor);\n border: var(--pf-c-hint--BorderWidth) solid var(--pf-c-hint--BorderColor);\n box-shadow: var(--pf-c-hint--BoxShadow); }\n .pf-c-hint .pf-c-button.pf-m-link.pf-m-inline {\n text-align: left;\n white-space: normal; }\n\n.pf-c-hint__actions {\n display: inline-grid;\n grid-auto-flow: column;\n margin-left: var(--pf-c-hint__actions--MarginLeft);\n text-align: right;\n grid-column: 2;\n grid-row: 1; }\n .pf-c-hint__actions .pf-c-dropdown .pf-c-dropdown__toggle.pf-m-plain {\n margin-top: var(--pf-c-hint__actions--c-dropdown--MarginTop); }\n .pf-c-hint__actions + .pf-c-hint__body {\n grid-column: 1; }\n\n.pf-c-hint__title {\n font-size: var(--pf-c-hint__title--FontSize); }\n\n.pf-c-hint__body {\n grid-column: 1 / -1;\n font-size: var(--pf-c-hint__body--FontSize); }\n\n.pf-c-hint__footer {\n grid-column: 1 / -1; }\n .pf-c-hint__footer > :not(:last-child) {\n margin-right: var(--pf-c-hint__footer--child--MarginRight); }\n\n.pf-c-inline-edit {\n --pf-c-inline-edit__group--item--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-inline-edit__action--c-button--m-valid--m-plain--Color: var(--pf-global--link--Color);\n --pf-c-inline-edit__action--c-button--m-valid--m-plain--hover--Color: var(--pf-global--link--Color--hover);\n --pf-c-inline-edit__action--m-icon-group--item--MarginRight: 0;\n --pf-c-inline-edit__group--m-footer--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-inline-edit__label--m-bold--FontWeight: var(--pf-global--FontWeight--semi-bold); }\n\n.pf-c-inline-edit__group {\n display: flex;\n align-items: baseline; }\n .pf-c-inline-edit__group > * {\n margin-right: var(--pf-c-inline-edit__group--item--MarginRight); }\n .pf-c-inline-edit__group.pf-m-icon-group {\n --pf-c-inline-edit__group--item--MarginRight: var(--pf-c-inline-edit__action--m-icon-group--item--MarginRight); }\n .pf-c-inline-edit__group.pf-m-footer {\n margin-top: var(--pf-c-inline-edit__group--m-footer--MarginTop); }\n .pf-c-inline-edit__group.pf-m-column {\n --pf-c-inline-edit__group--item--MarginRight: 0;\n flex-direction: column; }\n .pf-c-inline-edit__group > :last-child {\n --pf-c-inline-edit__group--item--MarginRight: 0; }\n\n.pf-c-inline-edit__input {\n flex: 1; }\n\n.pf-c-inline-edit__action.pf-m-valid .pf-c-button.pf-m-plain {\n --pf-c-button--m-plain--Color: var(--pf-c-inline-edit__action--c-button--m-valid--m-plain--Color); }\n .pf-c-inline-edit__action.pf-m-valid .pf-c-button.pf-m-plain:hover {\n --pf-c-button--m-plain--Color: var(--pf-c-inline-edit__action--c-button--m-valid--m-plain--hover--Color); }\n\n.pf-c-inline-edit__input,\n.pf-c-inline-edit__action,\n.pf-c-inline-edit__group.pf-m-action-group {\n display: none;\n visibility: hidden; }\n\n.pf-c-inline-edit__action.pf-m-enable-editable {\n display: inline-block;\n visibility: visible; }\n\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__input,\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__action,\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__input,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__action,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group {\n visibility: visible; }\n\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__input,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__input {\n display: block; }\n\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__action,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__action {\n display: inline-block; }\n\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__group.pf-m-action-group {\n display: inline-flex; }\n\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__value,\n.pf-c-inline-edit.pf-m-inline-editable .pf-c-inline-edit__action.pf-m-enable-editable,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__value,\n.pf-c-inline-edit .pf-m-inline-editable .pf-c-inline-edit__action.pf-m-enable-editable {\n display: none;\n visibility: hidden; }\n\n.pf-c-inline-edit__label + .pf-c-inline-edit__action.pf-m-enable > .pf-c-button {\n margin-top: calc(var(--pf-c-button--PaddingTop) * -1);\n margin-bottom: calc(var(--pf-c-button--PaddingBottom) * -1); }\n\n.pf-c-inline-edit__label.pf-m-bold {\n font-weight: var(--pf-c-inline-edit__label--m-bold--FontWeight); }\n\n.pf-c-input-group {\n --pf-c-input-group--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-input-group__text--FontSize: var(--pf-global--FontSize--md);\n --pf-c-input-group__text--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-input-group__text--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-input-group__text--Color: var(--pf-global--Color--dark-200);\n --pf-c-input-group__text--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-input-group__text--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-input-group__text--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-input-group__text--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-input-group__text--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-input-group__text--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-input-group__textarea--MinHeight: var(--pf-global--spacer--xl);\n --pf-c-input-group--c-form-control--invalid--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-input-group--c-form-control--MarginRight: 0;\n color: var(--pf-global--Color--100);\n display: flex;\n width: 100%;\n background-color: var(--pf-c-input-group--BackgroundColor); }\n .pf-c-input-group > * + * {\n margin-left: -1px; }\n .pf-c-input-group .pf-c-form-control[aria-invalid=\"true\"]:not(:last-child) {\n margin-right: var(--pf-c-input-group--c-form-control--MarginRight); }\n .pf-c-input-group input:not([type=\"checkbox\"]):not([type=\"radio\"]),\n .pf-c-input-group textarea {\n flex: 2;\n min-width: 0; }\n .pf-c-input-group textarea {\n min-height: var(--pf-c-input-group__textarea--MinHeight); }\n\n.pf-c-input-group__text {\n display: flex;\n align-items: center;\n padding-right: var(--pf-c-input-group__text--PaddingRight);\n padding-left: var(--pf-c-input-group__text--PaddingLeft);\n font-size: var(--pf-c-input-group__text--FontSize);\n color: var(--pf-c-input-group__text--Color);\n text-align: center;\n background-color: var(--pf-c-input-group__text--BackgroundColor);\n border: var(--pf-c-input-group__text--BorderWidth) solid;\n border-color: var(--pf-c-input-group__text--BorderTopColor) var(--pf-c-input-group__text--BorderRightColor) var(--pf-c-input-group__text--BorderBottomColor) var(--pf-c-input-group__text--BorderLeftColor); }\n label.pf-c-input-group__text {\n cursor: pointer; }\n .pf-c-input-group__text.pf-m-plain {\n --pf-c-input-group__text--BorderWidth: 0;\n margin-left: 0; }\n\n.pf-c-jump-links {\n --pf-c-jump-links__list--PaddingTop: 0;\n --pf-c-jump-links__list--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-jump-links__list--PaddingBottom: 0;\n --pf-c-jump-links__list--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-jump-links--m-vertical__list--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-jump-links--m-vertical__list--PaddingRight: 0;\n --pf-c-jump-links--m-vertical__list--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-jump-links--m-vertical__list--PaddingLeft: 0;\n --pf-c-jump-links__list--FlexDirection: row;\n --pf-c-jump-links--m-vertical__list--FlexDirection: column;\n --pf-c-jump-links__list--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-jump-links__list--before--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-jump-links__list--before--BorderRightWidth: 0;\n --pf-c-jump-links__list--before--BorderBottomWidth: 0;\n --pf-c-jump-links__list--before--BorderLeftWidth: 0;\n --pf-c-jump-links--m-vertical__list--before--BorderLeftWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-jump-links--m-vertical__list--before--BorderTopWidth: 0;\n --pf-c-jump-links__list__list--MarginTop: calc(var(--pf-global--spacer--sm) * -1);\n --pf-c-jump-links__link--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-jump-links__link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-jump-links__link--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-jump-links__link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-jump-links__list__list__link--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-jump-links__list__list__link--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-jump-links__list__list__link--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-jump-links__link--OutlineOffset: calc(-1 * var(--pf-global--spacer--sm));\n --pf-c-jump-links__link--before--BorderTopWidth: 0;\n --pf-c-jump-links__link--before--BorderRightWidth: 0;\n --pf-c-jump-links__link--before--BorderBottomWidth: 0;\n --pf-c-jump-links__link--before--BorderLeftWidth: 0;\n --pf-c-jump-links__link--before--BorderColor: transparent;\n --pf-c-jump-links__link--focus--before--BorderTopWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-jump-links__link--focus--before--BorderLeftWidth: 0;\n --pf-c-jump-links__link--focus--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-jump-links__item--m-current__link--before--BorderTopWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-jump-links__item--m-current__link--before--BorderLeftWidth: 0;\n --pf-c-jump-links__item--m-current__link--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-jump-links--m-vertical__link--focus--before--BorderTopWidth: 0;\n --pf-c-jump-links--m-vertical__link--focus--before--BorderLeftWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-jump-links--m-vertical__item--m-current__link--before--BorderTopWidth: 0;\n --pf-c-jump-links--m-vertical__item--m-current__link--before--BorderLeftWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-jump-links__link-text--Color: var(--pf-global--Color--200);\n --pf-c-jump-links__link--hover__link-text--Color: var(--pf-global--Color--100);\n --pf-c-jump-links__link--focus__link-text--Color: var(--pf-global--Color--100);\n --pf-c-jump-links__item--m-current__link-text--Color: var(--pf-global--Color--100);\n --pf-c-jump-links__label--MarginBottom: var(--pf-global--spacer--md);\n display: flex; }\n .pf-c-jump-links.pf-m-center {\n justify-content: center; }\n .pf-c-jump-links.pf-m-center .pf-c-jump-links__main {\n align-items: center; }\n .pf-c-jump-links.pf-m-vertical {\n --pf-c-jump-links__list--PaddingTop: var(--pf-c-jump-links--m-vertical__list--PaddingTop);\n --pf-c-jump-links__list--PaddingRight: var(--pf-c-jump-links--m-vertical__list--PaddingRight);\n --pf-c-jump-links__list--PaddingBottom: var(--pf-c-jump-links--m-vertical__list--PaddingBottom);\n --pf-c-jump-links__list--PaddingLeft: var(--pf-c-jump-links--m-vertical__list--PaddingLeft);\n --pf-c-jump-links__list--before--BorderTopWidth: var(--pf-c-jump-links--m-vertical__list--before--BorderTopWidth);\n --pf-c-jump-links__list--before--BorderLeftWidth: var(--pf-c-jump-links--m-vertical__list--before--BorderLeftWidth);\n --pf-c-jump-links__link--focus--before--BorderTopWidth: var(--pf-c-jump-links--m-vertical__link--focus--before--BorderTopWidth);\n --pf-c-jump-links__link--focus--before--BorderLeftWidth: var(--pf-c-jump-links--m-vertical__link--focus--before--BorderLeftWidth);\n --pf-c-jump-links__item--m-current__link--before--BorderTopWidth: var(--pf-c-jump-links--m-vertical__item--m-current__link--before--BorderTopWidth);\n --pf-c-jump-links__item--m-current__link--before--BorderLeftWidth: var(--pf-c-jump-links--m-vertical__item--m-current__link--before--BorderLeftWidth);\n --pf-c-jump-links__list--FlexDirection: var(--pf-c-jump-links--m-vertical__list--FlexDirection);\n flex-direction: column; }\n\n.pf-c-jump-links__list {\n position: relative;\n display: flex;\n flex-direction: var(--pf-c-jump-links__list--FlexDirection);\n padding-top: var(--pf-c-jump-links__list--PaddingTop);\n padding-right: var(--pf-c-jump-links__list--PaddingRight);\n padding-bottom: var(--pf-c-jump-links__list--PaddingBottom);\n padding-left: var(--pf-c-jump-links__list--PaddingLeft); }\n .pf-c-jump-links__list::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n content: \"\";\n border: solid var(--pf-c-jump-links__list--before--BorderColor);\n border-width: var(--pf-c-jump-links__list--before--BorderTopWidth) var(--pf-c-jump-links__list--before--BorderRightWidth) var(--pf-c-jump-links__list--before--BorderBottomWidth) var(--pf-c-jump-links__list--before--BorderLeftWidth); }\n .pf-c-jump-links__list .pf-c-jump-links__list {\n --pf-c-jump-links__list--PaddingTop: 0;\n --pf-c-jump-links__list--PaddingBottom: 0;\n --pf-c-jump-links__link--PaddingTop: var(--pf-c-jump-links__list__list__link--PaddingTop);\n --pf-c-jump-links__link--PaddingBottom: var(--pf-c-jump-links__list__list__link--PaddingBottom);\n --pf-c-jump-links__link--PaddingLeft: var(--pf-c-jump-links__list__list__link--PaddingLeft);\n margin-top: var(--pf-c-jump-links__list__list--MarginTop); }\n\n.pf-c-jump-links__link {\n position: relative;\n display: flex;\n flex: 1;\n padding-top: var(--pf-c-jump-links__link--PaddingTop);\n padding-right: var(--pf-c-jump-links__link--PaddingRight);\n padding-bottom: var(--pf-c-jump-links__link--PaddingBottom);\n padding-left: var(--pf-c-jump-links__link--PaddingLeft);\n text-decoration: none;\n outline-offset: var(--pf-c-jump-links__link--OutlineOffset); }\n .pf-c-jump-links__link:hover {\n --pf-c-jump-links__link-text--Color: var(--pf-c-jump-links__link--hover__link-text--Color); }\n .pf-c-jump-links__link:focus {\n --pf-c-jump-links__link-text--Color: var(--pf-c-jump-links__link--focus__link-text--Color);\n --pf-c-jump-links__link--before--BorderTopWidth: var(--pf-c-jump-links__link--focus--before--BorderTopWidth);\n --pf-c-jump-links__link--before--BorderLeftWidth: var(--pf-c-jump-links__link--focus--before--BorderLeftWidth);\n --pf-c-jump-links__link--before--BorderColor: var(--pf-c-jump-links__link--focus--before--BorderColor); }\n .pf-c-jump-links__link::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n content: \"\";\n border-color: var(--pf-c-jump-links__link--before--BorderColor);\n border-style: solid;\n border-width: var(--pf-c-jump-links__link--before--BorderTopWidth) var(--pf-c-jump-links__link--before--BorderRightWidth) var(--pf-c-jump-links__link--before--BorderBottomWidth) var(--pf-c-jump-links__link--before--BorderLeftWidth); }\n\n.pf-c-jump-links__item {\n --pf-c-jump-links__list--before--BorderColor: transparent; }\n .pf-c-jump-links__item.pf-m-current > .pf-c-jump-links__link {\n --pf-c-jump-links__link--before--BorderTopWidth: var(--pf-c-jump-links__item--m-current__link--before--BorderTopWidth);\n --pf-c-jump-links__link--before--BorderLeftWidth: var(--pf-c-jump-links__item--m-current__link--before--BorderLeftWidth);\n --pf-c-jump-links__link--before--BorderColor: var(--pf-c-jump-links__item--m-current__link--before--BorderColor);\n --pf-c-jump-links__link-text--Color: var(--pf-c-jump-links__item--m-current__link-text--Color); }\n\n.pf-c-jump-links__link-text {\n color: var(--pf-c-jump-links__link-text--Color); }\n\n.pf-c-jump-links__label {\n margin-bottom: var(--pf-c-jump-links__label--MarginBottom); }\n\n.pf-c-jump-links__main {\n display: flex;\n flex-direction: column; }\n\n.pf-c-label {\n --pf-c-label--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-label--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-label--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-label--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-label--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-label--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-label--Color: var(--pf-global--Color--100);\n --pf-c-label--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-label__content--before--BorderWidth: 0;\n --pf-c-label__content--before--BorderColor: transparent;\n --pf-c-label--m-outline--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-label--m-outline__content--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-label__content--link--hover--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-label__content--link--focus--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-global--BorderColor--200);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-global--BorderColor--200);\n --pf-c-label--m-outline__content--link--hover--before--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-label--m-outline__content--link--focus--before--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-label--m-blue--BackgroundColor: var(--pf-global--palette--blue-50);\n --pf-c-label--m-blue__content--Color: var(--pf-global--info-color--200);\n --pf-c-label--m-blue__icon--Color: var(--pf-global--primary-color--100);\n --pf-c-label--m-blue__content--link--hover--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-label--m-blue__content--link--focus--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-label--m-outline--m-blue__content--before--BorderColor: var(--pf-global--active-color--200);\n --pf-c-label--m-outline--m-blue__content--link--hover--before--BorderColor: var(--pf-global--active-color--200);\n --pf-c-label--m-outline--m-blue__content--link--focus--before--BorderColor: var(--pf-global--active-color--200);\n --pf-c-label--m-green--BackgroundColor: var(--pf-global--palette--green-50);\n --pf-c-label--m-green__content--Color: var(--pf-global--success-color--200);\n --pf-c-label--m-green__icon--Color: var(--pf-global--success-color--100);\n --pf-c-label--m-green__content--link--hover--before--BorderColor: var(--pf-global--success-color--100);\n --pf-c-label--m-green__content--link--focus--before--BorderColor: var(--pf-global--success-color--100);\n --pf-c-label--m-outline--m-green__content--before--BorderColor: var(--pf-global--palette--green-100);\n --pf-c-label--m-outline--m-green__content--link--hover--before--BorderColor: var(--pf-global--palette--green-100);\n --pf-c-label--m-outline--m-green__content--link--focus--before--BorderColor: var(--pf-global--palette--green-100);\n --pf-c-label--m-orange--BackgroundColor: var(--pf-global--palette--gold-50);\n --pf-c-label--m-orange__content--Color: var(--pf-global--palette--gold-700);\n --pf-c-label--m-orange__icon--Color: var(--pf-global--palette--orange-300);\n --pf-c-label--m-orange__content--link--hover--before--BorderColor: var(--pf-global--palette--orange-300);\n --pf-c-label--m-orange__content--link--focus--before--BorderColor: var(--pf-global--palette--orange-300);\n --pf-c-label--m-outline--m-orange__content--before--BorderColor: var(--pf-global--palette--gold-100);\n --pf-c-label--m-outline--m-orange__content--link--hover--before--BorderColor: var(--pf-global--palette--gold-100);\n --pf-c-label--m-outline--m-orange__content--link--focus--before--BorderColor: var(--pf-global--palette--gold-100);\n --pf-c-label--m-red--BackgroundColor: var(--pf-global--palette--red-50);\n --pf-c-label--m-red__content--Color: var(--pf-global--palette--red-300);\n --pf-c-label--m-red__icon--Color: var(--pf-global--danger-color--100);\n --pf-c-label--m-red__content--link--hover--before--BorderColor: var(--pf-global--danger-color--100);\n --pf-c-label--m-red__content--link--focus--before--BorderColor: var(--pf-global--danger-color--100);\n --pf-c-label--m-outline--m-red__content--before--BorderColor: var(--pf-global--danger-color--100);\n --pf-c-label--m-outline--m-red__content--link--hover--before--BorderColor: var(--pf-global--danger-color--100);\n --pf-c-label--m-outline--m-red__content--link--focus--before--BorderColor: var(--pf-global--danger-color--100);\n --pf-c-label--m-purple--BackgroundColor: var(--pf-global--palette--purple-50);\n --pf-c-label--m-purple__content--Color: var(--pf-global--palette--purple-700);\n --pf-c-label--m-purple__icon--Color: var(--pf-global--palette--purple-500);\n --pf-c-label--m-purple__content--link--hover--before--BorderColor: var(--pf-global--palette--purple-500);\n --pf-c-label--m-purple__content--link--focus--before--BorderColor: var(--pf-global--palette--purple-500);\n --pf-c-label--m-outline--m-purple__content--before--BorderColor: var(--pf-global--palette--purple-100);\n --pf-c-label--m-outline--m-purple__content--link--hover--before--BorderColor: var(--pf-global--palette--purple-100);\n --pf-c-label--m-outline--m-purple__content--link--focus--before--BorderColor: var(--pf-global--palette--purple-100);\n --pf-c-label--m-cyan--BackgroundColor: var(--pf-global--palette--cyan-50);\n --pf-c-label--m-cyan__content--Color: var(--pf-global--default-color--300);\n --pf-c-label--m-cyan__icon--Color: var(--pf-global--default-color--200);\n --pf-c-label--m-cyan__content--link--hover--before--BorderColor: var(--pf-global--default-color--200);\n --pf-c-label--m-cyan__content--link--focus--before--BorderColor: var(--pf-global--default-color--200);\n --pf-c-label--m-outline--m-cyan__content--before--BorderColor: var(--pf-global--palette--cyan-100);\n --pf-c-label--m-outline--m-cyan__content--link--hover--before--BorderColor: var(--pf-global--palette--cyan-100);\n --pf-c-label--m-outline--m-cyan__content--link--focus--before--BorderColor: var(--pf-global--palette--cyan-100);\n --pf-c-label--m-overflow__content--Color: var(--pf-global--link--Color);\n --pf-c-label--m-overflow__content--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-label--m-overflow__content--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-label--m-overflow__content--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-label--m-overflow__content--link--hover--before--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-label--m-overflow__content--link--hover--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-label--m-overflow__content--link--focus--before--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-label--m-overflow__content--link--focus--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-label__content--Color: var(--pf-global--Color--100);\n --pf-c-label__text--MaxWidth: 16ch;\n --pf-c-label__icon--Color: var(--pf-global--Color--100);\n --pf-c-label__icon--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-label__c-button--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-label__c-button--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-label__c-button--MarginRight: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-label__c-button--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-label__c-button--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-label__c-button--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-label__c-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-label__c-button--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-label__c-button--PaddingLeft: var(--pf-global--spacer--sm);\n position: relative;\n padding: var(--pf-c-label--PaddingTop) var(--pf-c-label--PaddingRight) var(--pf-c-label--PaddingBottom) var(--pf-c-label--PaddingLeft);\n font-size: var(--pf-c-label--FontSize);\n color: var(--pf-c-label--Color);\n white-space: nowrap;\n background-color: var(--pf-c-label--BackgroundColor);\n border: 0;\n border-radius: var(--pf-c-label--BorderRadius); }\n .pf-c-label.pf-m-blue {\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-blue--BackgroundColor);\n --pf-c-label__content--Color: var(--pf-c-label--m-blue__content--Color);\n --pf-c-label__icon--Color: var(--pf-c-label--m-blue__icon--Color);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-c-label--m-outline--m-blue__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-blue__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-blue__content--link--focus--before--BorderColor);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-c-label--m-outline--m-blue__content--link--hover--before--BorderColor);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-c-label--m-outline--m-blue__content--link--focus--before--BorderColor); }\n .pf-c-label.pf-m-green {\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-green--BackgroundColor);\n --pf-c-label__content--Color: var(--pf-c-label--m-green__content--Color);\n --pf-c-label__icon--Color: var(--pf-c-label--m-green__icon--Color);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-c-label--m-outline--m-green__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-green__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-green__content--link--focus--before--BorderColor);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-c-label--m-outline--m-green__content--link--hover--before--BorderColor);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-c-label--m-outline--m-green__content--link--focus--before--BorderColor); }\n .pf-c-label.pf-m-orange {\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-orange--BackgroundColor);\n --pf-c-label__content--Color: var(--pf-c-label--m-orange__content--Color);\n --pf-c-label__icon--Color: var(--pf-c-label--m-orange__icon--Color);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-c-label--m-outline--m-orange__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-orange__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-orange__content--link--focus--before--BorderColor);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-c-label--m-outline--m-orange__content--link--hover--before--BorderColor);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-c-label--m-outline--m-orange__content--link--focus--before--BorderColor); }\n .pf-c-label.pf-m-red {\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-red--BackgroundColor);\n --pf-c-label__content--Color: var(--pf-c-label--m-red__content--Color);\n --pf-c-label__icon--Color: var(--pf-c-label--m-red__icon--Color);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-c-label--m-outline--m-red__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-red__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-red__content--link--focus--before--BorderColor);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-c-label--m-outline--m-red__content--link--hover--before--BorderColor);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-c-label--m-outline--m-red__content--link--focus--before--BorderColor); }\n .pf-c-label.pf-m-purple {\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-purple--BackgroundColor);\n --pf-c-label__content--Color: var(--pf-c-label--m-purple__content--Color);\n --pf-c-label__icon--Color: var(--pf-c-label--m-purple__icon--Color);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-c-label--m-outline--m-purple__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-purple__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-purple__content--link--focus--before--BorderColor);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-c-label--m-outline--m-purple__content--link--hover--before--BorderColor);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-c-label--m-outline--m-purple__content--link--focus--before--BorderColor); }\n .pf-c-label.pf-m-cyan {\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-cyan--BackgroundColor);\n --pf-c-label__content--Color: var(--pf-c-label--m-cyan__content--Color);\n --pf-c-label__icon--Color: var(--pf-c-label--m-cyan__icon--Color);\n --pf-c-label--m-outline__content--before--BorderColor: var(--pf-c-label--m-outline--m-cyan__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-cyan__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-cyan__content--link--focus--before--BorderColor);\n --pf-c-label--m-outline__content--link--hover--before--BorderColor: var(--pf-c-label--m-outline--m-cyan__content--link--hover--before--BorderColor);\n --pf-c-label--m-outline__content--link--focus--before--BorderColor: var(--pf-c-label--m-outline--m-cyan__content--link--focus--before--BorderColor); }\n .pf-c-label.pf-m-outline {\n --pf-c-label__content--before--BorderWidth: var(--pf-c-label--m-outline__content--before--BorderWidth);\n --pf-c-label__content--before--BorderColor: var(--pf-c-label--m-outline__content--before--BorderColor);\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-outline--BackgroundColor); }\n .pf-c-label.pf-m-overflow:hover, .pf-c-label.pf-m-outline a.pf-c-label__content:hover,\n .pf-c-label.pf-m-outline button.pf-c-label__content:hover {\n --pf-c-label__content--before--BorderWidth: var(--pf-c-label--m-outline__content--link--hover--before--BorderWidth);\n --pf-c-label__content--before--BorderColor: var(--pf-c-label--m-outline__content--link--hover--before--BorderColor); }\n .pf-c-label.pf-m-overflow:focus, .pf-c-label.pf-m-outline a.pf-c-label__content:focus,\n .pf-c-label.pf-m-outline button.pf-c-label__content:focus {\n --pf-c-label__content--before--BorderWidth: var(--pf-c-label--m-outline__content--link--focus--before--BorderWidth);\n --pf-c-label__content--before--BorderColor: var(--pf-c-label--m-outline__content--link--focus--before--BorderColor); }\n .pf-c-label .pf-c-button {\n --pf-c-button--FontSize: var(--pf-c-label__c-button--FontSize);\n --pf-c-button--PaddingTop: var(--pf-c-label__c-button--PaddingTop);\n --pf-c-button--PaddingRight: var(--pf-c-label__c-button--PaddingRight);\n --pf-c-button--PaddingBottom: var(--pf-c-label__c-button--PaddingBottom);\n --pf-c-button--PaddingLeft: var(--pf-c-label__c-button--PaddingLeft);\n margin-top: var(--pf-c-label__c-button--MarginTop);\n margin-right: var(--pf-c-label__c-button--MarginRight);\n margin-bottom: var(--pf-c-label__c-button--MarginBottom);\n margin-left: var(--pf-c-label__c-button--MarginLeft); }\n .pf-c-label.pf-m-overflow {\n --pf-c-label__content--Color: var(--pf-c-label--m-overflow__content--Color);\n --pf-c-label--BackgroundColor: var(--pf-c-label--m-overflow__content--BackgroundColor);\n --pf-c-label__content--before--BorderWidth: var(--pf-c-label--m-overflow__content--before--BorderWidth);\n --pf-c-label__content--before--BorderColor: var(--pf-c-label--m-overflow__content--before--BorderColor);\n --pf-c-label__content--link--hover--before--BorderWidth: var(--pf-c-label--m-overflow__content--link--hover--before--BorderWidth);\n --pf-c-label__content--link--hover--before--BorderColor: var(--pf-c-label--m-overflow__content--link--hover--before--BorderColor);\n --pf-c-label__content--link--focus--before--BorderWidth: var(--pf-c-label--m-overflow__content--link--focus--before--BorderWidth);\n --pf-c-label__content--link--focus--before--BorderColor: var(--pf-c-label--m-overflow__content--link--focus--before--BorderColor); }\n\n.pf-c-label,\n.pf-c-label__content {\n display: inline-flex;\n align-items: center; }\n\n.pf-c-label__text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: var(--pf-c-label__text--MaxWidth); }\n\n.pf-c-label__content {\n color: var(--pf-c-label__content--Color);\n border: 0; }\n .pf-c-label__content::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-label__content--before--BorderWidth) solid var(--pf-c-label__content--before--BorderColor);\n border-radius: var(--pf-c-label--BorderRadius); }\n a.pf-c-label__content,\n button.pf-c-label__content {\n cursor: pointer;\n border: none; }\n a.pf-c-label__content, a.pf-c-label__content:hover, a.pf-c-label__content:focus,\n button.pf-c-label__content,\n button.pf-c-label__content:hover,\n button.pf-c-label__content:focus {\n text-decoration: none; }\n a.pf-c-label__content:hover,\n button.pf-c-label__content:hover {\n --pf-c-label__content--before--BorderWidth: var(--pf-c-label__content--link--hover--before--BorderWidth);\n --pf-c-label__content--before--BorderColor: var(--pf-c-label__content--link--hover--before--BorderColor); }\n a.pf-c-label__content:focus,\n button.pf-c-label__content:focus {\n --pf-c-label__content--before--BorderWidth: var(--pf-c-label__content--link--focus--before--BorderWidth);\n --pf-c-label__content--before--BorderColor: var(--pf-c-label__content--link--focus--before--BorderColor); }\n\n.pf-c-label__icon {\n margin-right: var(--pf-c-label__icon--MarginRight);\n color: var(--pf-c-label__icon--Color); }\n\n.pf-c-label-group {\n --pf-c-label-group__list--MarginBottom: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-label-group__list--MarginRight: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-label-group--m-category--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-label-group--m-category--PaddingRight: var(--pf-global--spacer--xs);\n --pf-c-label-group--m-category--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-label-group--m-category--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-label-group--m-vertical--m-category--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-label-group--m-category--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-label-group--m-category--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-label-group--m-category--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-label-group--m-category--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-label-group__label--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-label-group__label--MarginBottom: 0;\n --pf-c-label-group--m-vertical__label--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-label-group__label--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-label-group__label--MaxWidth: 18ch;\n --pf-c-label-group__close--MarginTop: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-label-group__close--MarginBottom: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-label-group--m-vertical__close--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-label-group--m-vertical__close--MarginRight: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-label-group--m-vertical__close--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-label-group--m-vertical__close--c-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-label-group--m-vertical__close--c-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-label-group__list-item--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-label-group__list-item--MarginBottom: var(--pf-global--spacer--xs);\n display: inline-flex; }\n .pf-c-label-group.pf-m-category {\n padding-top: var(--pf-c-label-group--m-category--PaddingTop);\n padding-right: var(--pf-c-label-group--m-category--PaddingRight);\n padding-bottom: var(--pf-c-label-group--m-category--PaddingBottom);\n padding-left: var(--pf-c-label-group--m-category--PaddingLeft);\n background-color: var(--pf-c-label-group--m-category--BackgroundColor);\n border: var(--pf-c-label-group--m-category--BorderWidth) solid var(--pf-c-label-group--m-category--BorderColor);\n border-radius: var(--pf-c-label-group--m-category--BorderRadius); }\n .pf-c-label-group.pf-m-vertical {\n --pf-c-label-group__list--MarginRight: 0;\n --pf-c-label-group__list--MarginBottom: 0;\n --pf-c-label-group__list-item--MarginRight: 0;\n --pf-c-label-group__label--MarginRight: 0;\n --pf-c-label-group__label--MarginBottom: var(--pf-c-label-group--m-vertical__label--MarginBottom);\n --pf-c-label-group__close--MarginTop: var(--pf-c-label-group--m-vertical__close--MarginTop);\n --pf-c-label-group__close--MarginLeft: var(--pf-c-label-group--m-vertical__close--MarginLeft);\n --pf-c-label-group__close--MarginBottom: 0;\n --pf-c-label-group__close--MarginRight: var(--pf-c-label-group--m-vertical__close--MarginRight);\n --pf-c-label-group--m-category--PaddingRight: var(--pf-c-label-group--m-vertical--m-category--PaddingRight); }\n .pf-c-label-group.pf-m-vertical.pf-c-label-group {\n align-items: flex-start; }\n .pf-c-label-group.pf-m-vertical .pf-c-label-group__list {\n flex-direction: column;\n align-items: flex-start; }\n .pf-c-label-group.pf-m-vertical .pf-c-label-group__main {\n flex-direction: column; }\n .pf-c-label-group.pf-m-vertical .pf-c-label-group__list-item:last-child {\n --pf-c-label-group__list-item--MarginBottom: 0; }\n .pf-c-label-group.pf-m-vertical .pf-c-label-group__close .pf-c-button {\n --pf-c-button--PaddingLeft: var(--pf-c-label-group--m-vertical__close--c-button--PaddingLeft);\n --pf-c-button--PaddingRight: var(--pf-c-label-group--m-vertical__close--c-button--PaddingRight); }\n\n.pf-c-label-group__main {\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n align-items: baseline; }\n\n.pf-c-label-group__list {\n display: inline-flex;\n flex-wrap: wrap;\n margin-right: var(--pf-c-label-group__list--MarginRight);\n margin-bottom: var(--pf-c-label-group__list--MarginBottom); }\n\n.pf-c-label-group__list-item {\n display: inline-flex;\n margin-right: var(--pf-c-label-group__list-item--MarginRight);\n margin-bottom: var(--pf-c-label-group__list-item--MarginBottom); }\n\n.pf-c-label-group__label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: var(--pf-c-label-group__label--MaxWidth);\n margin-right: var(--pf-c-label-group__label--MarginRight);\n margin-bottom: var(--pf-c-label-group__label--MarginBottom);\n font-size: var(--pf-c-label-group__label--FontSize); }\n\n.pf-c-label-group__close {\n margin-top: var(--pf-c-label-group__close--MarginTop);\n margin-right: var(--pf-c-label-group__close--MarginRight);\n margin-bottom: var(--pf-c-label-group__close--MarginBottom);\n margin-left: var(--pf-c-label-group__close--MarginLeft); }\n\n.pf-c-list {\n --pf-c-list--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-list--nested--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-list--nested--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-list--ul--ListStyle: var(--pf-global--ListStyle);\n --pf-c-list--li--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-list--m-inline--li--MarginRight: var(--pf-global--spacer--lg);\n padding-left: var(--pf-c-list--PaddingLeft); }\n .pf-c-list ol,\n .pf-c-list ul {\n margin-top: var(--pf-c-list--nested--MarginTop);\n margin-left: var(--pf-c-list--nested--MarginLeft); }\n .pf-c-list li + li {\n margin-top: var(--pf-c-list--li--MarginTop); }\n ul.pf-c-list:not(.pf-m-inline) {\n list-style: var(--pf-c-list--ul--ListStyle); }\n .pf-c-list.pf-m-inline {\n --pf-c-list--PaddingLeft: 0;\n display: flex;\n flex-wrap: wrap; }\n .pf-c-list.pf-m-inline li {\n --pf-c-list--li--MarginTop: 0; }\n .pf-c-list.pf-m-inline li:not(:last-child) {\n margin-right: var(--pf-c-list--m-inline--li--MarginRight); }\n\n.pf-c-login {\n --pf-c-login--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-login--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-login--xl--BackgroundImage: none;\n --pf-c-login__container--xl--GridColumnGap: var(--pf-global--spacer--3xl);\n --pf-c-login__container--MaxWidth: 31.25rem;\n --pf-c-login__container--xl--MaxWidth: none;\n --pf-c-login__container--PaddingLeft: 6.125rem;\n --pf-c-login__container--PaddingRight: 6.125rem;\n --pf-c-login__container--xl--GridTemplateColumns: 34rem minmax(auto, 34rem);\n --pf-c-login__header--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-login__header--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-login__header--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-login__header--xl--MarginBottom: var(--pf-global--spacer--2xl);\n --pf-c-login__header--xl--MarginTop: var(--pf-global--spacer--3xl);\n --pf-c-login__header--c-brand--MarginBottom: var(--pf-global--spacer--lg);\n --pf-c-login__header--c-brand--xl--MarginBottom: var(--pf-global--spacer--2xl);\n --pf-c-login__main--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-login__main--MarginBottom: var(--pf-global--spacer--lg);\n --pf-c-login__main-header--PaddingTop: var(--pf-global--spacer--2xl);\n --pf-c-login__main-header--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-login__main-header--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-login__main-header--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-login__main-header--md--PaddingRight: var(--pf-global--spacer--2xl);\n --pf-c-login__main-header--md--PaddingLeft: var(--pf-global--spacer--2xl);\n --pf-c-login__main-header--ColumnGap: var(--pf-global--spacer--md);\n --pf-c-login__main-header--RowGap: var(--pf-global--spacer--md);\n --pf-c-login__main-header-desc--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-login__main-header-desc--md--MarginBottom: 0;\n --pf-c-login__main-header-desc--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-login__main-body--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-login__main-body--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-login__main-body--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-login__main-body--md--PaddingRight: var(--pf-global--spacer--2xl);\n --pf-c-login__main-body--md--PaddingLeft: var(--pf-global--spacer--2xl);\n --pf-c-login__main-footer--PaddingBottom: var(--pf-global--spacer--3xl);\n --pf-c-login__main-footer--c-title--MarginBottom: var(--pf-global--spacer--md);\n --pf-c-login__main-footer-links--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-login__main-footer-links--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-login__main-footer-links--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-login__main-footer-links--PaddingLeft: var(--pf-global--spacer--3xl);\n --pf-c-login__main-footer-links-item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-login__main-footer-links-item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-login__main-footer-links-item--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-login__main-footer-links-item-link-svg--Fill: var(--pf-global--icon--Color--light);\n --pf-c-login__main-footer-links-item-link-svg--Width: var(--pf-global--icon--FontSize--lg);\n --pf-c-login__main-footer-links-item-link-svg--Height: var(--pf-global--icon--FontSize--lg);\n --pf-c-login__main-footer-links-item-link-svg--hover--Fill: var(--pf-global--icon--Color--dark);\n --pf-c-login__main-footer-band--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-login__main-footer-band--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-login__main-footer-band--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-login__main-footer-band--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-login__main-footer-band--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-login__main-footer-band-item--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-login__footer--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-login__footer--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-login__footer--c-list--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-login__footer--c-list--xl--PaddingTop: var(--pf-global--spacer--2xl);\n display: flex;\n justify-content: center;\n min-height: 100vh;\n padding-top: var(--pf-c-login--PaddingTop);\n padding-bottom: var(--pf-c-login--PaddingBottom); }\n @media (min-width: 1200px) {\n .pf-c-login {\n --pf-c-login__container--MaxWidth: var(--pf-c-login__container--xl--MaxWidth); } }\n @media (min-width: 576px) {\n .pf-c-login {\n --pf-c-login__header--PaddingRight: 0;\n --pf-c-login__header--PaddingLeft: 0; } }\n @media (min-width: 1200px) {\n .pf-c-login {\n --pf-c-login__header--MarginBottom: var(--pf-c-login__header--xl--MarginBottom);\n --pf-c-login__header--c-brand--MarginBottom: var(--pf-c-login__header--c-brand--xl--MarginBottom); } }\n @media (min-width: 1200px) {\n .pf-c-login {\n --pf-c-login__main--MarginBottom: 0; } }\n @media (min-width: 768px) {\n .pf-c-login {\n --pf-c-login__main-header--PaddingRight: var(--pf-c-login__main-header--md--PaddingRight);\n --pf-c-login__main-header--PaddingLeft: var(--pf-c-login__main-header--md--PaddingLeft);\n --pf-c-login__main-header-desc--MarginBottom: var(--pf-c-login__main-header-desc--md--MarginBottom); } }\n @media (min-width: 768px) {\n .pf-c-login {\n --pf-c-login__main-body--PaddingRight: var(--pf-c-login__main-body--md--PaddingRight);\n --pf-c-login__main-body--PaddingLeft: var(--pf-c-login__main-body--md--PaddingLeft); } }\n @media (min-width: 576px) {\n .pf-c-login {\n --pf-c-login__footer--PaddingRight: 0;\n --pf-c-login__footer--PaddingLeft: 0; } }\n @media (min-width: 1200px) {\n .pf-c-login {\n --pf-c-login__footer--c-list--PaddingTop: var(--pf-c-login__footer--c-list--xl--PaddingTop); } }\n @media (min-width: 1200px) {\n .pf-c-login {\n background-image: var(--pf-c-login--xl--BackgroundImage); } }\n @media (min-width: 576px) {\n .pf-c-login {\n align-items: center; } }\n\n.pf-c-login__container {\n width: 100%;\n max-width: var(--pf-c-login__container--MaxWidth); }\n @media (min-width: 1200px) {\n .pf-c-login__container {\n display: grid;\n justify-content: center;\n grid-column-gap: var(--pf-c-login__container--xl--GridColumnGap);\n grid-template-columns: var(--pf-c-login__container--xl--GridTemplateColumns);\n grid-template-areas: \"main header\" \"main footer\" \"main .\";\n padding-right: var(--pf-c-login__container--PaddingRight);\n padding-left: var(--pf-c-login__container--PaddingLeft); } }\n\n.pf-c-login__header {\n color: var(--pf-global--Color--100);\n grid-area: header;\n padding-right: var(--pf-c-login__header--PaddingRight);\n padding-left: var(--pf-c-login__header--PaddingLeft); }\n @media (min-width: 1200px) {\n .pf-c-login__header {\n margin-top: var(--pf-c-login__header--xl--MarginTop); } }\n .pf-c-login__header .pf-c-brand {\n margin-bottom: var(--pf-c-login__header--c-brand--MarginBottom); }\n\n.pf-c-login__main {\n margin-bottom: var(--pf-c-login__main--MarginBottom);\n background-color: var(--pf-c-login__main--BackgroundColor);\n grid-area: main; }\n .pf-c-login__main > :first-child:not(.pf-c-login__main-header) {\n padding-top: var(--pf-c-login__main-header--PaddingTop); }\n .pf-c-login__main > :last-child:not(.pf-c-login__main-footer) {\n padding-bottom: var(--pf-c-login__main-footer--PaddingBottom); }\n\n.pf-c-login__main-header {\n display: grid;\n grid-template-columns: 100%;\n column-gap: var(--pf-c-login__main-header--ColumnGap);\n row-gap: var(--pf-c-login__main-header--RowGap);\n align-items: center;\n padding: var(--pf-c-login__main-header--PaddingTop) var(--pf-c-login__main-header--PaddingRight) var(--pf-c-login__main-header--PaddingBottom) var(--pf-c-login__main-header--PaddingLeft); }\n @media (min-width: 768px) {\n .pf-c-login__main-header {\n grid-template-columns: 1fr auto; } }\n .pf-c-login__main-header .pf-c-dropdown {\n grid-column: auto;\n grid-row: auto; }\n @media (min-width: 768px) {\n .pf-c-login__main-header .pf-c-dropdown {\n grid-column: 2 / 3;\n grid-row: 1; } }\n\n.pf-c-login__main-header-desc {\n margin-bottom: var(--pf-c-login__main-header-desc--MarginBottom);\n font-size: var(--pf-c-login__main-header-desc--FontSize);\n grid-column: 1 / -1; }\n\n.pf-c-login__main-body {\n padding-right: var(--pf-c-login__main-body--PaddingRight);\n padding-bottom: var(--pf-c-login__main-body--PaddingBottom);\n padding-left: var(--pf-c-login__main-body--PaddingLeft); }\n\n.pf-c-login__main-footer {\n display: flex;\n flex-wrap: wrap; }\n .pf-c-login__main-footer .pf-c-title {\n margin-bottom: var(--pf-c-login__main-footer--c-title--MarginBottom);\n text-align: center; }\n .pf-c-login__main-footer > * {\n flex-basis: 100%; }\n\n.pf-c-login__main-footer-links {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n padding: var(--pf-c-login__main-footer-links--PaddingTop) var(--pf-c-login__main-footer-links--PaddingRight) var(--pf-c-login__main-footer-links--PaddingBottom) var(--pf-c-login__main-footer-links--PaddingLeft); }\n\n.pf-c-login__main-footer-links-item {\n padding-right: var(--pf-c-login__main-footer-links-item--PaddingRight);\n padding-left: var(--pf-c-login__main-footer-links-item--PaddingLeft);\n margin-bottom: var(--pf-c-login__main-footer-links-item--MarginBottom); }\n\n.pf-c-login__main-footer-links-item-link svg {\n fill: var(--pf-c-login__main-footer-links-item-link-svg--Fill);\n width: 100%;\n max-width: var(--pf-c-login__main-footer-links-item-link-svg--Width);\n height: 100%;\n max-height: var(--pf-c-login__main-footer-links-item-link-svg--Height); }\n\n.pf-c-login__main-footer-links-item-link:hover svg {\n fill: var(--pf-c-login__main-footer-links-item-link-svg--hover--Fill); }\n\n.pf-c-login__main-footer-band {\n padding: var(--pf-c-login__main-footer-band--PaddingTop) var(--pf-c-login__main-footer-band--PaddingRight) var(--pf-c-login__main-footer-band--PaddingBottom) var(--pf-c-login__main-footer-band--PaddingLeft);\n text-align: center;\n background-color: var(--pf-c-login__main-footer-band--BackgroundColor); }\n .pf-c-login__main-footer-band > * + * {\n padding-top: var(--pf-c-login__main-footer-band-item--PaddingTop); }\n\n.pf-c-login__footer {\n color: var(--pf-global--Color--100);\n grid-area: footer;\n padding-right: var(--pf-c-login__footer--PaddingRight);\n padding-left: var(--pf-c-login__footer--PaddingLeft); }\n .pf-c-login__footer .pf-c-list a {\n color: unset; }\n .pf-c-login__footer .pf-c-list:not(:only-child) {\n padding-top: var(--pf-c-login__footer--c-list--PaddingTop); }\n\n.pf-c-menu {\n color: var(--pf-global--Color--100);\n --pf-c-menu--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-menu--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-menu--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-menu--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-menu--m-flyout__menu--Top: calc(var(--pf-c-menu--PaddingTop) * -1);\n --pf-c-menu--c-divider--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-menu--c-divider--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-menu__search--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-menu__search--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-menu__search--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-menu__search--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-menu__list-item--Color: var(--pf-global--Color--100);\n --pf-c-menu__list-item--hover--Color: var(--pf-global--Color--100);\n --pf-c-menu__list-item--BackgroundColor: transparent;\n --pf-c-menu__list-item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-menu__item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-menu__item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-menu__item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-menu__item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-menu__item--OutlineOffset: calc(0.125rem * -1);\n --pf-c-menu__item--FontSize: var(--pf-global--FontSize--md);\n --pf-c-menu__item--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-menu__item--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-menu__item--disabled--Color: var(--pf-global--Color--dark-200);\n --pf-c-menu__group-title--PaddingTop: var(--pf-c-menu__item--PaddingTop);\n --pf-c-menu__group-title--PaddingRight: var(--pf-c-menu__item--PaddingRight);\n --pf-c-menu__group-title--PaddingBottom: var(--pf-c-menu__item--PaddingBottom);\n --pf-c-menu__group-title--PaddingLeft: var(--pf-c-menu__item--PaddingLeft);\n --pf-c-menu__group-title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-menu__group-title--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-menu__group-title--Color: var(--pf-global--Color--dark-200);\n --pf-c-menu__item-description--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-menu__item-description--Color: var(--pf-global--Color--200);\n --pf-c-menu__item-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-menu__item-toggle-icon--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-menu__item-toggle-icon--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-menu__item-text--item-toggle-icon--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-menu__item-toggle-icon--item-text--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-menu__item-select-icon--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-menu__item-select-icon--Color: var(--pf-global--active-color--100);\n --pf-c-menu__item-select-icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-menu__item-main__external-icon--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-menu__item-main__external-icon--Color: var(--pf-global--link--Color);\n --pf-c-menu__item-main__external-icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-menu__item-action--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-menu__item-action--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-menu__item-action--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-menu__item-action--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-menu__item-action-icon--Color: var(--pf-global--Color--dark-200);\n --pf-c-menu__item-action-icon--Height: calc(var(--pf-c-menu__item--FontSize) * var(--pf-c-menu__item--LineHeight));\n --pf-c-menu__item-action--hover__icon--Color: var(--pf-global--Color--dark-100);\n --pf-c-menu__item-action--m-favorite__icon--Color: var(--pf-global--disabled-color--200);\n --pf-c-menu__item-action--m-favorite__icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-menu__item-action--m-favorite--m-favorited__icon--Color: var(--pf-global--palette--gold-400);\n --pf-c-menu--m-drilldown--Width: auto;\n --pf-c-menu--m-drilldown--Height: auto;\n --pf-c-menu--m-drilldown--TransitionDuration--transform: var(--pf-global--TransitionDuration);\n --pf-c-menu--m-drilldown--TransitionDuration--height: var(--pf-global--TransitionDuration);\n --pf-c-menu--m-drilldown--Transition: transform var(--pf-c-menu--m-drilldown--TransitionDuration--transform), height var(--pf-c-menu--m-drilldown--TransitionDuration--height);\n --pf-c-menu--m-drilldown--c-menu--Top: calc(var(--pf-c-menu--PaddingTop) * -1);\n --pf-c-menu--m-drilldown--c-menu--TransitionDuration--transform: var(--pf-global--TransitionDuration);\n --pf-c-menu--m-drilldown--c-menu--TransitionDuration--visibility: var(--pf-global--TransitionDuration);\n --pf-c-menu--m-drilldown--c-menu--Transition: transform var(--pf-c-menu--m-drilldown--c-menu--TransitionDuration--transform), visibility var(--pf-c-menu--m-drilldown--c-menu--TransitionDuration--visibility);\n --pf-c-menu--m-drilldown__list--TransitionDuration--transform: var(--pf-global--TransitionDuration);\n --pf-c-menu--m-drilldown__list--Transition: transform var(--pf-c-menu--m-drilldown__list--TransitionDuration--transform);\n --pf-c-menu--m-drilled-in--c-menu__list-item--m-current-path--c-menu--ZIndex: var(--pf-global--ZIndex--2xl);\n padding-top: var(--pf-c-menu--PaddingTop);\n padding-bottom: var(--pf-c-menu--PaddingBottom);\n background-color: var(--pf-c-menu--BackgroundColor);\n box-shadow: var(--pf-c-menu--BoxShadow); }\n .pf-c-menu.pf-m-flyout .pf-c-menu {\n position: absolute;\n top: var(--pf-c-menu--m-flyout__menu--Top);\n left: 100%; }\n .pf-c-menu.pf-m-flyout .pf-c-menu__list-item {\n position: relative; }\n .pf-c-menu.pf-m-drilldown {\n width: var(--pf-c-menu--m-drilldown--Width);\n height: var(--pf-c-menu--m-drilldown--Height);\n overflow: hidden;\n transition: var(--pf-c-menu--m-drilldown--Transition); }\n .pf-c-menu.pf-m-drilldown.pf-m-drilled-in > .pf-c-menu__content > .pf-c-menu__list,\n .pf-c-menu.pf-m-drilldown.pf-m-drilled-in > .pf-c-menu__list {\n transform: translateX(-100%); }\n .pf-c-menu.pf-m-drilldown .pf-c-menu {\n --pf-c-menu--BoxShadow: none;\n position: absolute;\n top: var(--pf-c-menu--m-drilldown--c-menu--Top);\n left: 100%;\n width: 100%;\n transition: var(--pf-c-menu--m-drilldown--c-menu--Transition); }\n .pf-c-menu.pf-m-drilldown .pf-c-menu.pf-m-drilled-in {\n transform: translateX(-100%); }\n .pf-c-menu.pf-m-drilldown .pf-c-menu__list {\n position: relative;\n transition: var(--pf-c-menu--m-drilldown__list--Transition); }\n .pf-c-menu.pf-m-drilldown .pf-c-menu__list-item.pf-m-current-path .pf-c-menu {\n z-index: var(--pf-c-menu--m-drilled-in--c-menu__list-item--m-current-path--c-menu--ZIndex); }\n .pf-c-menu.pf-m-drilldown .pf-c-menu__list-item:not(.pf-m-current-path) .pf-c-menu {\n visibility: hidden; }\n .pf-c-menu.pf-m-drilldown .pf-c-menu__item {\n outline-offset: var(--pf-c-menu__item--OutlineOffset); }\n .pf-c-menu .pf-c-divider {\n margin-top: var(--pf-c-menu--c-divider--MarginTop);\n margin-bottom: var(--pf-c-menu--c-divider--MarginBottom); }\n\n.pf-c-menu__search {\n padding-top: var(--pf-c-menu__search--PaddingTop);\n padding-right: var(--pf-c-menu__search--PaddingRight);\n padding-bottom: var(--pf-c-menu__search--PaddingBottom);\n padding-left: var(--pf-c-menu__search--PaddingLeft); }\n\n.pf-c-menu__list-item {\n display: flex;\n color: var(--pf-c-menu__list-item--Color);\n background-color: var(--pf-c-menu__list-item--BackgroundColor); }\n .pf-c-menu__list-item:hover:not(.pf-m-disabled), .pf-c-menu__list-item:focus-within:not(.pf-m-disabled) {\n --pf-c-menu__list-item--Color: var(--pf-c-menu__list-item--hover--Color);\n --pf-c-menu__list-item--BackgroundColor: var(--pf-c-menu__list-item--hover--BackgroundColor); }\n .pf-c-menu__list-item:hover:not(.pf-m-disabled) .pf-c-menu__item-external-icon, .pf-c-menu__list-item:focus-within:not(.pf-m-disabled) .pf-c-menu__item-external-icon {\n opacity: 1; }\n .pf-c-menu__list-item.pf-m-disabled .pf-c-menu__item {\n --pf-c-menu__item--Color: var(--pf-c-menu__item--disabled--Color);\n pointer-events: none; }\n\n.pf-c-menu__item {\n display: flex;\n flex-basis: 100%;\n flex-direction: column;\n min-width: 0;\n padding-top: var(--pf-c-menu__item--PaddingTop);\n padding-right: var(--pf-c-menu__item--PaddingRight);\n padding-bottom: var(--pf-c-menu__item--PaddingBottom);\n padding-left: var(--pf-c-menu__item--PaddingLeft);\n font-size: var(--pf-c-menu__item--FontSize);\n font-weight: var(--pf-c-menu__item--FontWeight);\n line-height: var(--pf-c-menu__item--LineHeight);\n color: var(--pf-c-menu__item--Color);\n text-align: left;\n background-color: var(--pf-c-menu__item--BackgroundColor);\n border: none; }\n .pf-c-menu__item:hover {\n text-decoration: none; }\n .pf-c-menu__item:disabled {\n --pf-c-menu__item--Color: var(--pf-c-menu__item--disabled--Color);\n pointer-events: none; }\n .pf-c-menu__item.pf-m-selected .pf-c-menu__item-select-icon {\n opacity: 1; }\n\n.pf-c-menu__item-main {\n display: flex;\n align-items: center;\n width: 100%; }\n .pf-c-menu__item-main .pf-c-menu__item-external-icon {\n margin-left: var(--pf-c-menu__item-main__external-icon--MarginLeft);\n font-size: var(--pf-c-menu__item-main__external-icon--FontSize);\n color: var(--pf-c-menu__item-main__external-icon--Color);\n opacity: 0; }\n\n.pf-c-menu__item-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex-grow: 1; }\n\n.pf-c-menu__group-title {\n padding-top: var(--pf-c-menu__group-title--PaddingTop);\n padding-right: var(--pf-c-menu__group-title--PaddingRight);\n padding-bottom: var(--pf-c-menu__group-title--PaddingBottom);\n padding-left: var(--pf-c-menu__group-title--PaddingLeft);\n font-size: var(--pf-c-menu__group-title--FontSize);\n font-weight: var(--pf-c-menu__group-title--FontWeight);\n color: var(--pf-c-menu__group-title--Color); }\n\n.pf-c-menu__item-description {\n font-size: var(--pf-c-menu__item-description--FontSize);\n color: var(--pf-c-menu__item-description--Color);\n word-break: break-all; }\n\n.pf-c-menu__item-icon {\n margin-right: var(--pf-c-menu__item-icon--MarginRight); }\n\n.pf-c-menu__item-toggle-icon {\n padding-right: var(--pf-c-menu__item-toggle-icon--PaddingRight);\n padding-left: var(--pf-c-menu__item-toggle-icon--PaddingLeft); }\n\n.pf-c-menu__item-text + .pf-c-menu__item-toggle-icon {\n margin-left: var(--pf-c-menu__item-text--item-toggle-icon--MarginLeft); }\n\n.pf-c-menu__item-toggle-icon + .pf-c-menu__item-text {\n margin-left: var(--pf-c-menu__item-toggle-icon--item-text--MarginLeft); }\n\n.pf-c-menu__item-select-icon {\n margin-left: var(--pf-c-menu__item-select-icon--MarginLeft);\n font-size: var(--pf-c-menu__item-select-icon--FontSize);\n color: var(--pf-c-menu__item-select-icon--Color);\n opacity: 0; }\n\n.pf-c-menu__item-action {\n display: flex;\n padding-top: var(--pf-c-menu__item-action--PaddingTop);\n padding-right: var(--pf-c-menu__item-action--PaddingRight);\n padding-bottom: var(--pf-c-menu__item-action--PaddingBottom);\n padding-left: var(--pf-c-menu__item-action--PaddingLeft);\n border: none; }\n .pf-c-menu__item-action:hover, .pf-c-menu__item-action:focus {\n --pf-c-menu__item-action-icon--Color: var(--pf-c-menu__item-action--hover__icon--Color); }\n .pf-c-menu__item-action.pf-m-favorite {\n --pf-c-menu__item-action-icon--Color: var(--pf-c-menu__item-action--m-favorite__icon--Color); }\n .pf-c-menu__item-action.pf-m-favorite.pf-m-favorited {\n --pf-c-menu__item-action-icon--Color: var(--pf-c-menu__item-action--m-favorite--m-favorited__icon--Color); }\n .pf-c-menu__item-action.pf-m-favorite .pf-c-menu__item-action-icon {\n font-size: var(--pf-c-menu__item-action--m-favorite__icon--FontSize); }\n\n.pf-c-menu__item-action-icon {\n display: flex;\n align-items: center;\n height: var(--pf-c-menu__item-action-icon--Height);\n color: var(--pf-c-menu__item-action-icon--Color); }\n\n.pf-c-modal-box {\n --pf-c-modal-box--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-modal-box--BoxShadow: var(--pf-global--BoxShadow--xl);\n --pf-c-modal-box--ZIndex: var(--pf-global--ZIndex--xl);\n --pf-c-modal-box--Width: 100%;\n --pf-c-modal-box--MaxWidth: calc(100% - var(--pf-global--spacer--xl));\n --pf-c-modal-box--m-sm--sm--MaxWidth: 35rem;\n --pf-c-modal-box--m-md--Width: 52.5rem;\n --pf-c-modal-box--m-lg--lg--MaxWidth: 70rem;\n --pf-c-modal-box--MaxHeight: calc(100% - var(--pf-global--spacer--2xl));\n --pf-c-modal-box--m-align-top--spacer: var(--pf-global--spacer--sm);\n --pf-c-modal-box--m-align-top--xl--spacer: var(--pf-global--spacer--xl);\n --pf-c-modal-box--m-align-top--MarginTop: var(--pf-c-modal-box--m-align-top--spacer);\n --pf-c-modal-box--m-align-top--MaxHeight: calc(100% - min(var(--pf-c-modal-box--m-align-top--spacer), var(--pf-global--spacer--2xl)) - var(--pf-c-modal-box--m-align-top--spacer));\n --pf-c-modal-box--m-align-top--MaxWidth: calc(100% - min(var(--pf-c-modal-box--m-align-top--spacer) * 2, var(--pf-global--spacer--xl)));\n --pf-c-modal-box--m-danger__title-icon--Color: var(--pf-global--danger-color--100);\n --pf-c-modal-box--m-warning__title-icon--Color: var(--pf-global--warning-color--100);\n --pf-c-modal-box--m-success__title-icon--Color: var(--pf-global--success-color--100);\n --pf-c-modal-box--m-info__title-icon--Color: var(--pf-global--info-color--100);\n --pf-c-modal-box--m-default__title-icon--Color: var(--pf-global--default-color--200);\n --pf-c-modal-box__header--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-modal-box__header--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-modal-box__header--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-modal-box__header--last-child--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-modal-box__title--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-modal-box__title--FontFamily: var(--pf-global--FontFamily--heading--sans-serif);\n --pf-c-modal-box__title--FontSize: var(--pf-global--FontSize--2xl);\n --pf-c-modal-box__title-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-modal-box__title-icon--Color: var(--pf-global--Color--100);\n --pf-c-modal-box__description--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-modal-box__body--MinHeight: calc(var(--pf-global--FontSize--md) * var(--pf-global--LineHeight--md));\n --pf-c-modal-box__body--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-modal-box__body--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-modal-box__body--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-modal-box__body--last-child--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-modal-box__header--body--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-modal-box--c-button--Top: calc(var(--pf-global--spacer--lg));\n --pf-c-modal-box--c-button--Right: var(--pf-global--spacer--md);\n --pf-c-modal-box--c-button--sibling--MarginRight: calc(var(--pf-global--spacer--xl) + var(--pf-global--spacer--sm));\n --pf-c-modal-box__footer--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-modal-box__footer--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-modal-box__footer--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-modal-box__footer--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-modal-box__footer--c-button--MarginRight: var(--pf-global--spacer--md);\n --pf-c-modal-box__footer--c-button--sm--MarginRight: calc(var(--pf-c-modal-box__footer--c-button--MarginRight) / 2);\n position: relative;\n z-index: var(--pf-c-modal-box--ZIndex);\n display: flex;\n flex-direction: column;\n width: var(--pf-c-modal-box--Width);\n max-width: var(--pf-c-modal-box--MaxWidth);\n max-height: var(--pf-c-modal-box--MaxHeight);\n background-color: var(--pf-c-modal-box--BackgroundColor);\n box-shadow: var(--pf-c-modal-box--BoxShadow); }\n @media (min-width: 1200px) {\n .pf-c-modal-box {\n --pf-c-modal-box--m-align-top--spacer: var(--pf-c-modal-box--m-align-top--xl--spacer); } }\n .pf-c-modal-box.pf-m-sm {\n --pf-c-modal-box--Width: var(--pf-c-modal-box--m-sm--sm--MaxWidth); }\n .pf-c-modal-box.pf-m-md {\n --pf-c-modal-box--Width: var(--pf-c-modal-box--m-md--Width); }\n .pf-c-modal-box.pf-m-lg {\n --pf-c-modal-box--Width: var(--pf-c-modal-box--m-lg--lg--MaxWidth); }\n .pf-c-modal-box.pf-m-align-top {\n top: var(--pf-c-modal-box--m-align-top--MarginTop);\n align-self: flex-start;\n max-width: var(--pf-c-modal-box--m-align-top--MaxWidth);\n max-height: var(--pf-c-modal-box--m-align-top--MaxHeight); }\n .pf-c-modal-box.pf-m-danger {\n --pf-c-modal-box__title-icon--Color: var(--pf-c-modal-box--m-danger__title-icon--Color); }\n .pf-c-modal-box.pf-m-warning {\n --pf-c-modal-box__title-icon--Color: var(--pf-c-modal-box--m-warning__title-icon--Color); }\n .pf-c-modal-box.pf-m-success {\n --pf-c-modal-box__title-icon--Color: var(--pf-c-modal-box--m-success__title-icon--Color); }\n .pf-c-modal-box.pf-m-default {\n --pf-c-modal-box__title-icon--Color: var(--pf-c-modal-box--m-default__title-icon--Color); }\n .pf-c-modal-box.pf-m-info {\n --pf-c-modal-box__title-icon--Color: var(--pf-c-modal-box--m-info__title-icon--Color); }\n .pf-c-modal-box > .pf-c-button {\n position: absolute;\n top: var(--pf-c-modal-box--c-button--Top);\n right: var(--pf-c-modal-box--c-button--Right); }\n .pf-c-modal-box > .pf-c-button + * {\n margin-right: var(--pf-c-modal-box--c-button--sibling--MarginRight); }\n\n.pf-c-modal-box__header {\n display: flex;\n flex-direction: column;\n padding-top: var(--pf-c-modal-box__header--PaddingTop);\n padding-right: var(--pf-c-modal-box__header--PaddingRight);\n padding-left: var(--pf-c-modal-box__header--PaddingLeft); }\n .pf-c-modal-box__header.pf-m-help {\n display: flex;\n flex-direction: row; }\n .pf-c-modal-box__header:last-child {\n padding-bottom: var(--pf-c-modal-box__header--last-child--PaddingBottom); }\n .pf-c-modal-box__header + .pf-c-modal-box__body {\n --pf-c-modal-box__body--PaddingTop: var(--pf-c-modal-box__header--body--PaddingTop); }\n\n.pf-c-modal-box__header-main {\n flex-grow: 1;\n min-width: 0; }\n\n.pf-c-modal-box__title,\n.pf-c-modal-box__title-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n\n.pf-c-modal-box__title {\n flex: 0 0 auto;\n font-family: var(--pf-c-modal-box__title--FontFamily);\n font-size: var(--pf-c-modal-box__title--FontSize);\n line-height: var(--pf-c-modal-box__title--LineHeight); }\n .pf-c-modal-box__title.pf-m-icon {\n display: flex; }\n\n.pf-c-modal-box__title-icon {\n margin-right: var(--pf-c-modal-box__title-icon--MarginRight);\n color: var(--pf-c-modal-box__title-icon--Color); }\n\n.pf-c-modal-box__description {\n padding-top: var(--pf-c-modal-box__description--PaddingTop); }\n\n.pf-c-modal-box__body {\n flex: 1 1 auto;\n min-height: var(--pf-c-modal-box__body--MinHeight);\n padding-top: var(--pf-c-modal-box__body--PaddingTop);\n padding-right: var(--pf-c-modal-box__body--PaddingRight);\n padding-left: var(--pf-c-modal-box__body--PaddingLeft);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n word-break: break-word;\n -webkit-overflow-scrolling: touch; }\n .pf-c-modal-box__body:last-child {\n padding-bottom: var(--pf-c-modal-box__body--last-child--PaddingBottom); }\n\n.pf-c-modal-box__footer {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n padding-top: var(--pf-c-modal-box__footer--PaddingTop);\n padding-right: var(--pf-c-modal-box__footer--PaddingRight);\n padding-bottom: var(--pf-c-modal-box__footer--PaddingBottom);\n padding-left: var(--pf-c-modal-box__footer--PaddingLeft); }\n .pf-c-modal-box__footer > .pf-c-button:not(:last-child) {\n margin-right: var(--pf-c-modal-box__footer--c-button--MarginRight); }\n @media screen and (min-width: 576px) {\n .pf-c-modal-box__footer > .pf-c-button:not(:last-child) {\n --pf-c-modal-box__footer--c-button--MarginRight: var(--pf-c-modal-box__footer--c-button--sm--MarginRight); } }\n\n.pf-c-nav {\n --pf-c-nav--Transition: var(--pf-global--Transition);\n --pf-c-nav__item--m-expanded__toggle-icon--Rotate: 90deg;\n --pf-c-nav--m-light__item--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-nav--m-light__item--m-current--not--m-expanded__link--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-nav--m-light__link--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-light__link--hover--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-light__link--focus--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-light__link--active--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-light__link--m-current--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-light__link--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-nav--m-light__link--focus--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-nav--m-light__link--active--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-nav--m-light__link--m-current--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-nav--m-light__link--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-nav--m-light__link--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-nav--m-light__link--m-current--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-nav--m-light__section-title--Color: var(--pf-global--Color--dark-200);\n --pf-c-nav--m-light__section-title--BorderBottomColor: var(--pf-global--BorderColor--300);\n --pf-c-nav--m-light--c-divider--BackgroundColor: var(--pf-global--BorderColor--300);\n --pf-c-nav--m-light__subnav__link--hover--after--BorderColor: var(--pf-global--BorderColor--dark-100);\n --pf-c-nav--m-light__subnav__link--focus--after--BorderColor: var(--pf-global--BorderColor--dark-100);\n --pf-c-nav--m-light__subnav__link--active--after--BorderColor: var(--pf-global--BorderColor--dark-100);\n --pf-c-nav--m-light__subnav__link--m-current--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-nav__item--MarginTop: 0;\n --pf-c-nav__item--m-current--not--m-expanded__link--BackgroundColor: var(--pf-global--BackgroundColor--dark-400);\n --pf-c-nav__link--m-current--not--m-expanded__link--after--BorderWidth: var(--pf-global--BorderWidth--xl);\n --pf-c-nav__item--before--BorderColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__item--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__link--FontSize: var(--pf-global--FontSize--md);\n --pf-c-nav__link--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-nav__link--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-nav__link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-nav__link--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-nav__link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-nav__link--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-nav__link--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-nav__link--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__link--hover--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__link--focus--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__link--active--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__link--m-current--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__link--BackgroundColor: transparent;\n --pf-c-nav__link--hover--BackgroundColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__link--focus--BackgroundColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__link--active--BackgroundColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__link--m-current--BackgroundColor: var(--pf-global--BackgroundColor--dark-400);\n --pf-c-nav__link--OutlineOffset: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-nav__link--before--BorderColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__link--before--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__link--hover--before--BorderBottomWidth: 0;\n --pf-c-nav__link--focus--before--BorderBottomWidth: 0;\n --pf-c-nav__link--active--before--BorderBottomWidth: 0;\n --pf-c-nav__link--m-current--before--BorderBottomWidth: 0;\n --pf-c-nav__link--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__link--hover--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__link--focus--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__link--active--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__link--m-current--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__link--after--BorderLeftWidth: 0;\n --pf-c-nav__link--hover--after--BorderLeftWidth: 0;\n --pf-c-nav__link--focus--after--BorderLeftWidth: 0;\n --pf-c-nav__link--active--after--BorderLeftWidth: 0;\n --pf-c-nav__link--m-current--after--BorderLeftWidth: var(--pf-global--BorderWidth--xl);\n --pf-c-nav--m-horizontal__link--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-nav--m-horizontal__link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-nav--m-horizontal__link--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-nav--m-horizontal__link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-nav--m-horizontal__link--lg--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-nav--m-horizontal__link--lg--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-nav--m-horizontal__link--Right: var(--pf-global--spacer--md);\n --pf-c-nav--m-horizontal__link--Left: var(--pf-global--spacer--md);\n --pf-c-nav--m-horizontal__link--Color: var(--pf-global--Color--light-300);\n --pf-c-nav--m-horizontal__link--hover--Color: var(--pf-global--active-color--400);\n --pf-c-nav--m-horizontal__link--focus--Color: var(--pf-global--active-color--400);\n --pf-c-nav--m-horizontal__link--active--Color: var(--pf-global--active-color--400);\n --pf-c-nav--m-horizontal__link--m-current--Color: var(--pf-global--active-color--400);\n --pf-c-nav--m-horizontal__link--BackgroundColor: transparent;\n --pf-c-nav--m-horizontal__link--hover--BackgroundColor: transparent;\n --pf-c-nav--m-horizontal__link--focus--BackgroundColor: transparent;\n --pf-c-nav--m-horizontal__link--active--BackgroundColor: transparent;\n --pf-c-nav--m-horizontal__link--m-current--BackgroundColor: transparent;\n --pf-c-nav--m-horizontal__link--before--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav--m-horizontal__link--before--BorderWidth: 0;\n --pf-c-nav--m-horizontal__link--hover--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-horizontal__link--focus--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-horizontal__link--active--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-horizontal__link--m-current--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-tertiary__link--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-nav--m-tertiary__link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-nav--m-tertiary__link--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-nav--m-tertiary__link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-nav--m-tertiary__link--Right: var(--pf-global--spacer--md);\n --pf-c-nav--m-tertiary__link--Left: var(--pf-global--spacer--md);\n --pf-c-nav--m-tertiary__link--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-tertiary__link--hover--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__link--focus--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__link--active--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__link--m-current--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__link--BackgroundColor: transparent;\n --pf-c-nav--m-tertiary__link--hover--BackgroundColor: transparent;\n --pf-c-nav--m-tertiary__link--focus--BackgroundColor: transparent;\n --pf-c-nav--m-tertiary__link--active--BackgroundColor: transparent;\n --pf-c-nav--m-tertiary__link--m-current--BackgroundColor: transparent;\n --pf-c-nav--m-tertiary__link--before--BorderColor: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__link--before--BorderWidth: 0;\n --pf-c-nav--m-tertiary__link--hover--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-tertiary__link--focus--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-tertiary__link--active--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-tertiary__link--m-current--before--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-nav--m-tertiary__scroll-button--Color: var(--pf-global--Color--dark-100);\n --pf-c-nav--m-tertiary__scroll-button--hover--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__scroll-button--focus--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__scroll-button--active--Color: var(--pf-global--active-color--100);\n --pf-c-nav--m-tertiary__scroll-button--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-nav--m-tertiary__scroll-button--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-nav--m-tertiary__scroll-button--disabled--before--BorderColor: var(--pf-global--disabled-color--300);\n --pf-c-nav__subnav--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-nav__subnav--xl--PaddingLeft: var(--pf-c-nav__link--PaddingLeft);\n --pf-c-nav__subnav__link--MarginTop: 0;\n --pf-c-nav__subnav__link--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-nav__subnav__link--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-nav__subnav__link--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-nav__subnav__link--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-nav__subnav__link--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-nav__subnav__link--hover--after--BorderColor: var(--pf-global--BorderColor--200);\n --pf-c-nav__subnav__link--focus--after--BorderColor: var(--pf-global--BorderColor--200);\n --pf-c-nav__subnav__link--active--after--BorderColor: var(--pf-global--BorderColor--200);\n --pf-c-nav__subnav__link--m-current--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__subnav__link--hover--after--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__subnav__link--focus--after--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__subnav__link--active--after--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__subnav__link--m-current--after--BorderWidth: var(--pf-global--BorderWidth--xl);\n --pf-c-nav__subnav--MaxHeight: 0;\n --pf-c-nav__item--m-expanded__subnav--MaxHeight: 100%;\n --pf-c-nav__subnav--c-divider--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-nav__subnav--c-divider--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-nav__section--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-nav__section__item--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-nav__section__link--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-nav__section__link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-nav__section__link--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-nav__section__link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-nav__section__link--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-nav__section__link--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-nav__section__link--FontSize: var(--pf-global--FontSize--md);\n --pf-c-nav__section__link--before--BorderBottomWidth: 0;\n --pf-c-nav__section__link--hover--after--BorderColor: transparent;\n --pf-c-nav__section__link--focus--after--BorderColor: transparent;\n --pf-c-nav__section__link--active--after--BorderColor: transparent;\n --pf-c-nav__section__link--m-current--after--BorderColor: var(--pf-global--active-color--400);\n --pf-c-nav__section__link--hover--after--BorderWidth: 0;\n --pf-c-nav__section__link--focus--after--BorderWidth: 0;\n --pf-c-nav__section__link--active--after--BorderWidth: 0;\n --pf-c-nav__section__link--m-current--after--BorderWidth: var(--pf-global--BorderWidth--xl);\n --pf-c-nav__section--section--MarginTop: var(--pf-global--spacer--xl);\n --pf-c-nav__section-title--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-nav__section-title--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-nav__section-title--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-nav__section-title--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-nav__section-title--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-nav__section-title--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-nav__section-title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-nav__section-title--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__section-title--BorderBottomColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__section-title--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__scroll-button--Color: var(--pf-global--Color--light-100);\n --pf-c-nav__scroll-button--hover--Color: var(--pf-global--active-color--400);\n --pf-c-nav__scroll-button--focus--Color: var(--pf-global--active-color--400);\n --pf-c-nav__scroll-button--active--Color: var(--pf-global--active-color--400);\n --pf-c-nav__scroll-button--disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-nav__scroll-button--BackgroundColor: transparent;\n --pf-c-nav__scroll-button--Width: var(--pf-global--target-size--MinWidth);\n --pf-c-nav__scroll-button--OutlineOffset: calc(-1 * var(--pf-global--spacer--xs));\n --pf-c-nav__scroll-button--Transition: margin .125s, transform .125s, opacity .125s;\n --pf-c-nav__scroll-button--before--BorderColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-nav__scroll-button--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-nav__scroll-button--before--BorderRightWidth: 0;\n --pf-c-nav__scroll-button--before--BorderLeftWidth: 0;\n --pf-c-nav__scroll-button--disabled--before--BorderColor: transparent;\n --pf-c-nav__toggle--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-nav__toggle--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-nav__toggle--FontSize: var(--pf-global--icon--FontSize--md);\n --pf-c-nav__toggle-icon--Transition: var(--pf-global--TransitionDuration);\n --pf-c-nav--c-divider--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-nav--c-divider--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-nav--c-divider--PaddingRight: 0;\n --pf-c-nav--c-divider--PaddingLeft: 0;\n --pf-c-nav--c-divider--BackgroundColor: var(--pf-global--BackgroundColor--dark-200); }\n @media screen and (min-width: 1200px) {\n .pf-c-nav {\n --pf-c-nav__link--PaddingRight: var(--pf-c-nav__link--xl--PaddingRight);\n --pf-c-nav__link--PaddingLeft: var(--pf-c-nav__link--xl--PaddingLeft);\n --pf-c-nav__section__link--PaddingRight: var(--pf-c-nav__section__link--xl--PaddingRight);\n --pf-c-nav__section__link--PaddingLeft: var(--pf-c-nav__section__link--xl--PaddingLeft);\n --pf-c-nav__section-title--PaddingRight: var(--pf-c-nav__section-title--xl--PaddingRight);\n --pf-c-nav__section-title--PaddingLeft: var(--pf-c-nav__section-title--xl--PaddingLeft);\n --pf-c-nav__subnav--PaddingLeft: var(--pf-c-nav__subnav--xl--PaddingLeft); } }\n .pf-c-nav.pf-m-horizontal, .pf-c-nav.pf-m-tertiary {\n overflow: hidden; }\n .pf-c-nav.pf-m-horizontal,\n .pf-c-nav.pf-m-horizontal .pf-c-nav__list, .pf-c-nav.pf-m-tertiary,\n .pf-c-nav.pf-m-tertiary .pf-c-nav__list {\n position: relative;\n display: flex; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__list, .pf-c-nav.pf-m-tertiary .pf-c-nav__list {\n flex: 1;\n max-width: 100%;\n overflow-x: auto;\n white-space: nowrap;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: -ms-autohiding-scrollbar; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__list::-webkit-scrollbar, .pf-c-nav.pf-m-tertiary .pf-c-nav__list::-webkit-scrollbar {\n display: none; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__item, .pf-c-nav.pf-m-tertiary .pf-c-nav__item {\n display: flex; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__link, .pf-c-nav.pf-m-tertiary .pf-c-nav__link {\n align-items: center;\n align-self: stretch;\n white-space: nowrap; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__link::before, .pf-c-nav.pf-m-tertiary .pf-c-nav__link::before {\n top: auto;\n bottom: 0; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__link::after, .pf-c-nav.pf-m-tertiary .pf-c-nav__link::after {\n content: none; }\n .pf-c-nav.pf-m-horizontal .pf-c-nav__link::before {\n right: var(--pf-c-nav--m-horizontal__link--Right);\n left: var(--pf-c-nav--m-horizontal__link--Left); }\n .pf-c-nav.pf-m-tertiary .pf-c-nav__link::before {\n right: var(--pf-c-nav--m-tertiary__link--Right);\n left: var(--pf-c-nav--m-tertiary__link--Left); }\n .pf-c-nav.pf-m-light {\n --pf-c-nav__item--before--BorderColor: var(--pf-c-nav--m-light__item--before--BorderColor);\n --pf-c-nav__item--m-current--not--m-expanded__link--BackgroundColor: var(--pf-c-nav--m-light__item--m-current--not--m-expanded__link--BackgroundColor);\n --pf-c-nav__link--Color: var(--pf-c-nav--m-light__link--Color);\n --pf-c-nav__link--hover--Color: var(--pf-c-nav--m-light__link--hover--Color);\n --pf-c-nav__link--focus--Color: var(--pf-c-nav--m-light__link--focus--Color);\n --pf-c-nav__link--active--Color: var(--pf-c-nav--m-light__link--active--Color);\n --pf-c-nav__link--m-current--Color: var(--pf-c-nav--m-light__link--m-current--Color);\n --pf-c-nav__link--hover--BackgroundColor: var(--pf-c-nav--m-light__link--hover--BackgroundColor);\n --pf-c-nav__link--focus--BackgroundColor: var(--pf-c-nav--m-light__link--focus--BackgroundColor);\n --pf-c-nav__link--active--BackgroundColor: var(--pf-c-nav--m-light__link--active--BackgroundColor);\n --pf-c-nav__link--m-current--BackgroundColor: var(--pf-c-nav--m-light__link--m-current--BackgroundColor);\n --pf-c-nav__link--before--BorderColor: var(--pf-c-nav--m-light__link--before--BorderColor);\n --pf-c-nav__link--after--BorderColor: var(--pf-c-nav--m-light__link--after--BorderColor);\n --pf-c-nav__link--m-current--after--BorderColor: var(--pf-c-nav--m-light__link--m-current--after--BorderColor);\n --pf-c-nav__subnav__link--hover--after--BorderColor: var(--pf-c-nav--m-light__subnav__link--hover--after--BorderColor);\n --pf-c-nav__subnav__link--focus--after--BorderColor: var(--pf-c-nav--m-light__subnav__link--focus--after--BorderColor);\n --pf-c-nav__subnav__link--active--after--BorderColor: var(--pf-c-nav--m-light__subnav__link--active--after--BorderColor);\n --pf-c-nav__subnav__link--m-current--after--BorderColor: var(--pf-c-nav--m-light__subnav__link--m-current--after--BorderColor);\n --pf-c-nav__section-title--Color: var(--pf-c-nav--m-light__section-title--Color);\n --pf-c-nav__section-title--BorderBottomColor: var(--pf-c-nav--m-light__section-title--BorderBottomColor); }\n .pf-c-nav.pf-m-light .pf-c-divider {\n --pf-c-divider--after--BackgroundColor: var(--pf-c-nav--m-light--c-divider--BackgroundColor); }\n .pf-c-nav.pf-m-horizontal {\n --pf-c-nav__link--PaddingTop: var(--pf-c-nav--m-horizontal__link--PaddingTop);\n --pf-c-nav__link--PaddingRight: var(--pf-c-nav--m-horizontal__link--PaddingRight);\n --pf-c-nav__link--PaddingBottom: var(--pf-c-nav--m-horizontal__link--PaddingBottom);\n --pf-c-nav__link--PaddingLeft: var(--pf-c-nav--m-horizontal__link--PaddingLeft);\n --pf-c-nav__link--Right: var(--pf-c-nav--m-horizontal__link--Right);\n --pf-c-nav__link--Left: var(--pf-c-nav--m-horizontal__link--Left);\n --pf-c-nav__link--Color: var(--pf-c-nav--m-horizontal__link--Color);\n --pf-c-nav__link--hover--Color: var(--pf-c-nav--m-horizontal__link--hover--Color);\n --pf-c-nav__link--active--Color: var(--pf-c-nav--m-horizontal__link--active--Color);\n --pf-c-nav__link--focus--Color: var(--pf-c-nav--m-horizontal__link--focus--Color);\n --pf-c-nav__link--m-current--Color: var(--pf-c-nav--m-horizontal__link--m-current--Color);\n --pf-c-nav__link--BackgroundColor: var(--pf-c-nav--m-horizontal__link--BackgroundColor);\n --pf-c-nav__link--hover--BackgroundColor: var(--pf-c-nav--m-horizontal__link--hover--BackgroundColor);\n --pf-c-nav__link--focus--BackgroundColor: var(--pf-c-nav--m-horizontal__link--focus--BackgroundColor);\n --pf-c-nav__link--active--BackgroundColor: var(--pf-c-nav--m-horizontal__link--active--BackgroundColor);\n --pf-c-nav__link--m-current--BackgroundColor: var(--pf-c-nav--m-horizontal__link--m-current--BackgroundColor);\n --pf-c-nav__link--before--BorderColor: var(--pf-c-nav--m-horizontal__link--before--BorderColor);\n --pf-c-nav__link--before--BorderBottomWidth: var(--pf-c-nav--m-horizontal__link--before--BorderWidth);\n --pf-c-nav__link--hover--before--BorderBottomWidth: var(--pf-c-nav--m-horizontal__link--hover--before--BorderWidth);\n --pf-c-nav__link--focus--before--BorderBottomWidth: var(--pf-c-nav--m-horizontal__link--focus--before--BorderWidth);\n --pf-c-nav__link--active--before--BorderBottomWidth: var(--pf-c-nav--m-horizontal__link--active--before--BorderWidth);\n --pf-c-nav__link--m-current--before--BorderBottomWidth: var(--pf-c-nav--m-horizontal__link--m-current--before--BorderWidth); }\n .pf-c-nav.pf-m-tertiary {\n --pf-c-nav__link--PaddingTop: var(--pf-c-nav--m-tertiary__link--PaddingTop);\n --pf-c-nav__link--PaddingRight: var(--pf-c-nav--m-tertiary__link--PaddingRight);\n --pf-c-nav__link--PaddingBottom: var(--pf-c-nav--m-tertiary__link--PaddingBottom);\n --pf-c-nav__link--PaddingLeft: var(--pf-c-nav--m-tertiary__link--PaddingLeft);\n --pf-c-nav__link--Right: var(--pf-c-nav--m-tertiary__link--Right);\n --pf-c-nav__link--Left: var(--pf-c-nav--m-tertiary__link--Left);\n --pf-c-nav__link--Color: var(--pf-c-nav--m-tertiary__link--Color);\n --pf-c-nav__link--hover--Color: var(--pf-c-nav--m-tertiary__link--hover--Color);\n --pf-c-nav__link--active--Color: var(--pf-c-nav--m-tertiary__link--active--Color);\n --pf-c-nav__link--focus--Color: var(--pf-c-nav--m-tertiary__link--focus--Color);\n --pf-c-nav__link--m-current--Color: var(--pf-c-nav--m-tertiary__link--m-current--Color);\n --pf-c-nav__link--BackgroundColor: var(--pf-c-nav--m-tertiary__link--BackgroundColor);\n --pf-c-nav__link--hover--BackgroundColor: var(--pf-c-nav--m-tertiary__link--hover--BackgroundColor);\n --pf-c-nav__link--focus--BackgroundColor: var(--pf-c-nav--m-tertiary__link--focus--BackgroundColor);\n --pf-c-nav__link--active--BackgroundColor: var(--pf-c-nav--m-tertiary__link--active--BackgroundColor);\n --pf-c-nav__link--m-current--BackgroundColor: var(--pf-c-nav--m-tertiary__link--m-current--BackgroundColor);\n --pf-c-nav__link--before--BorderColor: var(--pf-c-nav--m-tertiary__link--before--BorderColor);\n --pf-c-nav__link--before--BorderBottomWidth: var(--pf-c-nav--m-tertiary__link--before--BorderWidth);\n --pf-c-nav__link--hover--before--BorderBottomWidth: var(--pf-c-nav--m-tertiary__link--hover--before--BorderWidth);\n --pf-c-nav__link--focus--before--BorderBottomWidth: var(--pf-c-nav--m-tertiary__link--focus--before--BorderWidth);\n --pf-c-nav__link--active--before--BorderBottomWidth: var(--pf-c-nav--m-tertiary__link--active--before--BorderWidth);\n --pf-c-nav__link--m-current--before--BorderBottomWidth: var(--pf-c-nav--m-tertiary__link--m-current--before--BorderWidth);\n --pf-c-nav__scroll-button--Color: var(--pf-c-nav--m-tertiary__scroll-button--Color);\n --pf-c-nav__scroll-button--hover--Color: var(--pf-c-nav--m-tertiary__scroll-button--hover--Color);\n --pf-c-nav__scroll-button--focus--Color: var(--pf-c-nav--m-tertiary__scroll-button--focus--Color);\n --pf-c-nav__scroll-button--active--Color: var(--pf-c-nav--m-tertiary__scroll-button--active--Color);\n --pf-c-nav__scroll-button--disabled--Color: var(--pf-c-nav--m-tertiary__scroll-button--disabled--Color);\n --pf-c-nav__scroll-button--before--BorderColor: var(--pf-c-nav--m-tertiary__scroll-button--before--BorderColor);\n --pf-c-nav__scroll-button--disabled--before--BorderColor: var(--pf-c-nav--m-tertiary__scroll-button--disabled--before--BorderColor); }\n .pf-c-nav .pf-c-divider {\n --pf-c-divider--after--BackgroundColor: var(--pf-c-nav--c-divider--BackgroundColor);\n padding-right: var(--pf-c-nav--c-divider--PaddingRight);\n padding-left: var(--pf-c-nav--c-divider--PaddingLeft);\n margin-top: var(--pf-c-nav--c-divider--MarginTop);\n margin-bottom: var(--pf-c-nav--c-divider--MarginBottom); }\n .pf-c-nav.pf-m-scrollable .pf-c-nav__scroll-button {\n opacity: 1; }\n .pf-c-nav.pf-m-scrollable .pf-c-nav__scroll-button:nth-of-type(1) {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-nav.pf-m-scrollable .pf-c-nav__scroll-button:nth-of-type(2) {\n margin-left: 0;\n transform: translateX(0); }\n\n.pf-c-nav__list {\n display: block; }\n\n.pf-c-nav__item {\n position: relative;\n margin-top: var(--pf-c-nav__item--MarginTop); }\n .pf-c-nav__item.pf-m-expandable {\n --pf-c-nav__link--before--BorderBottomWidth: 0; }\n .pf-c-nav__item.pf-m-expandable::before {\n position: absolute;\n right: 0;\n bottom: calc(var(--pf-c-nav__item--before--BorderWidth) * -1);\n left: 0;\n content: \"\";\n border-bottom: var(--pf-c-nav__item--before--BorderWidth) solid var(--pf-c-nav__item--before--BorderColor); }\n\n.pf-c-nav__link {\n position: relative;\n display: flex;\n align-items: baseline;\n padding: var(--pf-c-nav__link--PaddingTop) var(--pf-c-nav__link--PaddingRight) var(--pf-c-nav__link--PaddingBottom) var(--pf-c-nav__link--PaddingLeft);\n font-size: var(--pf-c-nav__link--FontSize);\n font-weight: var(--pf-c-nav__link--FontWeight);\n color: var(--pf-c-nav__link--Color);\n background-color: var(--pf-c-nav__link--BackgroundColor);\n outline-offset: var(--pf-c-nav__link--OutlineOffset); }\n .pf-c-nav__link::after, .pf-c-nav__link::before {\n position: absolute;\n content: \"\";\n border: 0 solid; }\n .pf-c-nav__link::before {\n right: 0;\n bottom: calc(var(--pf-c-nav__link--before--BorderBottomWidth) * -1);\n left: 0;\n border-color: var(--pf-c-nav__link--before--BorderColor);\n border-bottom-width: var(--pf-c-nav__link--before--BorderBottomWidth); }\n .pf-c-nav__link::after {\n top: 0;\n bottom: 0;\n left: 0;\n border: 0 solid;\n border-color: var(--pf-c-nav__link--after--BorderColor);\n border-left-width: var(--pf-c-nav__link--after--BorderLeftWidth); }\n .pf-c-nav__link:hover {\n color: var(--pf-c-nav__link--hover--Color);\n background-color: var(--pf-c-nav__link--hover--BackgroundColor); }\n .pf-c-nav__link:hover::before {\n border-bottom-width: var(--pf-c-nav__link--hover--before--BorderBottomWidth); }\n .pf-c-nav__link:hover::after {\n border-color: var(--pf-c-nav__link--hover--after--BorderColor);\n border-left-width: var(--pf-c-nav__link--hover--after--BorderLeftWidth); }\n .pf-c-nav__link:focus {\n color: var(--pf-c-nav__link--focus--Color);\n background-color: var(--pf-c-nav__link--focus--BackgroundColor); }\n .pf-c-nav__link:focus::before {\n border-bottom-width: var(--pf-c-nav__link--focus--before--BorderBottomWidth); }\n .pf-c-nav__link:focus::after {\n border-color: var(--pf-c-nav__link--focus--after--BorderColor);\n border-left-width: var(--pf-c-nav__link--focus--after--BorderLeftWidth); }\n .pf-c-nav__link:active {\n color: var(--pf-c-nav__link--active--Color);\n background-color: var(--pf-c-nav__link--active--BackgroundColor); }\n .pf-c-nav__link:active::before {\n border-bottom-width: var(--pf-c-nav__link--active--before--BorderBottomWidth); }\n .pf-c-nav__link:active::after {\n border-color: var(--pf-c-nav__link--active--after--BorderColor);\n border-left-width: var(--pf-c-nav__link--active--after--BorderLeftWidth); }\n .pf-c-nav__link.pf-m-current, .pf-c-nav__link.pf-m-current:hover,\n .pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link {\n color: var(--pf-c-nav__link--m-current--Color);\n background-color: var(--pf-c-nav__link--m-current--BackgroundColor); }\n .pf-c-nav__link.pf-m-current::before, .pf-c-nav__link.pf-m-current:hover::before,\n .pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link::before {\n border-bottom-width: var(--pf-c-nav__link--m-current--before--BorderBottomWidth); }\n .pf-c-nav__link.pf-m-current::after, .pf-c-nav__link.pf-m-current:hover::after,\n .pf-c-nav__item.pf-m-current:not(.pf-m-expanded) .pf-c-nav__link::after {\n border-color: var(--pf-c-nav__link--m-current--after--BorderColor);\n border-left-width: var(--pf-c-nav__link--m-current--after--BorderLeftWidth); }\n .pf-c-nav__link, .pf-c-nav__link:hover, .pf-c-nav__link:focus, .pf-c-nav__link:active {\n width: 100%;\n text-decoration: none;\n border: none; }\n\n.pf-c-nav__subnav {\n --pf-c-nav__link--PaddingTop: var(--pf-c-nav__subnav__link--PaddingTop);\n --pf-c-nav__link--PaddingRight: var(--pf-c-nav__subnav__link--PaddingRight);\n --pf-c-nav__link--PaddingBottom: var(--pf-c-nav__subnav__link--PaddingBottom);\n --pf-c-nav__link--PaddingLeft: var(--pf-c-nav__subnav__link--PaddingLeft);\n --pf-c-nav__link--FontSize: var(--pf-c-nav__subnav__link--FontSize);\n --pf-c-nav__link--hover--after--BorderColor: var(--pf-c-nav__subnav__link--hover--after--BorderColor);\n --pf-c-nav__link--focus--after--BorderColor: var(--pf-c-nav__subnav__link--focus--after--BorderColor);\n --pf-c-nav__link--active--after--BorderColor: var(--pf-c-nav__subnav__link--active--after--BorderColor);\n --pf-c-nav__link--m-current--after--BorderColor: var(--pf-c-nav__subnav__link--m-current--after--BorderColor);\n --pf-c-nav__link--hover--after--BorderLeftWidth: var(--pf-c-nav__subnav__link--hover--after--BorderWidth);\n --pf-c-nav__link--focus--after--BorderLeftWidth: var(--pf-c-nav__subnav__link--focus--after--BorderWidth);\n --pf-c-nav__link--active--after--BorderLeftWidth: var(--pf-c-nav__subnav__link--active--after--BorderWidth);\n --pf-c-nav__link--m-current--after--BorderLeftWidth: var(--pf-c-nav__subnav__link--m-current--after--BorderWidth);\n --pf-c-nav--c-divider--PaddingRight: var(--pf-c-nav__subnav--c-divider--PaddingRight);\n --pf-c-nav--c-divider--PaddingLeft: var(--pf-c-nav__subnav--c-divider--PaddingLeft);\n max-height: var(--pf-c-nav__subnav--MaxHeight);\n padding-bottom: var(--pf-c-nav__subnav--PaddingBottom);\n padding-left: var(--pf-c-nav__subnav--PaddingLeft);\n transition: var(--pf-c-nav--Transition);\n scrollbar-width: none;\n -ms-overflow-style: -ms-autohiding-scrollbar; }\n .pf-c-nav__item.pf-m-expanded .pf-c-nav__subnav {\n --pf-c-nav__subnav--MaxHeight: var(--pf-c-nav__item--m-expanded__subnav--MaxHeight);\n overflow-y: auto;\n opacity: 1; }\n .pf-c-nav__subnav::-webkit-scrollbar {\n display: none; }\n\n.pf-c-nav__toggle {\n flex: none;\n padding-right: var(--pf-c-nav__toggle--PaddingRight);\n padding-left: var(--pf-c-nav__toggle--PaddingLeft);\n margin-left: auto;\n font-size: var(--pf-c-nav__toggle--FontSize);\n line-height: 1; }\n\n.pf-c-nav__toggle-icon {\n display: inline-block;\n transition: var(--pf-c-nav__toggle-icon--Transition); }\n .pf-c-nav__item.pf-m-expanded .pf-c-nav__toggle-icon {\n transform: rotate(var(--pf-c-nav__item--m-expanded__toggle-icon--Rotate)); }\n\n.pf-c-nav__section {\n --pf-c-nav__item--MarginTop: var(--pf-c-nav__section__item--MarginTop);\n --pf-c-nav__link--PaddingTop: var(--pf-c-nav__section__link--PaddingTop);\n --pf-c-nav__link--PaddingRight: var(--pf-c-nav__section__link--PaddingRight);\n --pf-c-nav__link--PaddingBottom: var(--pf-c-nav__section__link--PaddingBottom);\n --pf-c-nav__link--PaddingLeft: var(--pf-c-nav__section__link--PaddingLeft);\n --pf-c-nav__link--FontSize: var(--pf-c-nav__section__link--FontSize);\n --pf-c-nav__link--before--BorderBottomWidth: var(--pf-c-nav__section__link--before--BorderBottomWidth);\n --pf-c-nav__link--hover--after--BorderColor: var(--pf-c-nav__section__link--hover--after--BorderColor);\n --pf-c-nav__link--focus--after--BorderColor: var(--pf-c-nav__section__link--focus--after--BorderColor);\n --pf-c-nav__link--active--after--BorderColor: var(--pf-c-nav__section__link--active--after--BorderColor);\n --pf-c-nav__link--m-current--after--BorderColor: var(--pf-c-nav__section__link--m-current--after--BorderColor);\n --pf-c-nav__link--hover--after--BorderLeftWidth: var(--pf-c-nav__section__link--hover--after--BorderWidth);\n --pf-c-nav__link--focus--after--BorderLeftWidth: var(--pf-c-nav__section__link--focus--after--BorderWidth);\n --pf-c-nav__link--active--after--BorderLeftWidth: var(--pf-c-nav__section__link--active--after--BorderWidth);\n --pf-c-nav__link--m-current--after--BorderLeftWidth: var(--pf-c-nav__section__link--m-current--after--BorderWidth);\n margin-top: var(--pf-c-nav__section--MarginTop);\n --pf-c-nav--c-divider--MarginBottom: 0; }\n .pf-c-nav__section + .pf-c-nav__section {\n --pf-c-nav__section--MarginTop: var(--pf-c-nav__section--section--MarginTop); }\n\n.pf-c-nav__section-title {\n padding: var(--pf-c-nav__section-title--PaddingTop) var(--pf-c-nav__section-title--PaddingRight) var(--pf-c-nav__section-title--PaddingBottom) var(--pf-c-nav__section-title--PaddingLeft);\n font-size: var(--pf-c-nav__section-title--FontSize);\n color: var(--pf-c-nav__section-title--Color);\n border-bottom: var(--pf-c-nav__section-title--BorderBottomWidth) solid var(--pf-c-nav__section-title--BorderBottomColor); }\n\n.pf-c-nav__scroll-button {\n flex: none;\n width: var(--pf-c-nav__scroll-button--Width);\n color: var(--pf-c-nav__scroll-button--Color);\n background-color: var(--pf-c-nav__scroll-button--BackgroundColor);\n border: 0;\n outline-offset: var(--pf-c-nav__scroll-button--OutlineOffset);\n opacity: 0;\n transition: var(--pf-c-nav__scroll-button--Transition); }\n .pf-c-nav__scroll-button::before {\n position: absolute;\n top: 0;\n bottom: 0;\n content: \"\";\n border: solid var(--pf-c-nav__scroll-button--before--BorderColor);\n border-width: 0 var(--pf-c-nav__scroll-button--before--BorderRightWidth) 0 var(--pf-c-nav__scroll-button--before--BorderLeftWidth); }\n .pf-c-nav__scroll-button:hover {\n color: var(--pf-c-nav__scroll-button--hover--Color); }\n .pf-c-nav__scroll-button:focus {\n color: var(--pf-c-nav__scroll-button--focus--Color); }\n .pf-c-nav__scroll-button:active {\n color: var(--pf-c-nav__scroll-button--active--Color); }\n .pf-c-nav__scroll-button:disabled {\n color: var(--pf-c-nav__scroll-button--disabled--Color);\n border-color: var(--pf-c-nav__scroll-button--disabled--before--BorderColor); }\n .pf-c-nav__scroll-button:nth-of-type(1) {\n --pf-c-nav__scroll-button--before--BorderRightWidth: var(--pf-c-nav__scroll-button--before--BorderWidth);\n margin-right: calc(var(--pf-c-nav__scroll-button--Width) * -1);\n transform: translateX(-100%); }\n .pf-c-nav__scroll-button:nth-of-type(1)::before {\n right: 0; }\n .pf-c-nav__scroll-button:nth-of-type(2) {\n --pf-c-nav__scroll-button--before--BorderLeftWidth: var(--pf-c-nav__scroll-button--before--BorderWidth);\n margin-left: calc(var(--pf-c-nav__scroll-button--Width) * -1);\n transform: translateX(100%); }\n .pf-c-nav__scroll-button:nth-of-type(2)::before {\n left: 0; }\n\n.pf-c-notification-badge {\n --pf-c-notification-badge--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-notification-badge--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-notification-badge--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-notification-badge--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-notification-badge--MarginTop: calc(-1 * var(--pf-global--spacer--form-element));\n --pf-c-notification-badge--MarginRight: calc(-1 * var(--pf-global--spacer--md));\n --pf-c-notification-badge--MarginBottom: calc(-1 * var(--pf-global--spacer--form-element));\n --pf-c-notification-badge--MarginLeft: calc(-1 * var(--pf-global--spacer--md));\n --pf-c-notification-badge--after--BorderColor: transparent;\n --pf-c-notification-badge--after--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-notification-badge--after--BorderWidth: 0;\n --pf-c-notification-badge--after--Top: 0;\n --pf-c-notification-badge--after--Right: 0;\n --pf-c-notification-badge--after--Width: auto;\n --pf-c-notification-badge--after--Height: auto;\n --pf-c-notification-badge--after--BackgroundColor: transparent;\n --pf-c-notification-badge--after--TranslateX: 0;\n --pf-c-notification-badge--after--TranslateY: 0;\n --pf-c-notification-badge__i--Width: auto;\n --pf-c-notification-badge__i--Height: auto;\n --pf-c-notification-badge--m-read--after--BorderColor: transparent;\n --pf-c-notification-badge--m-read--after--BackgroundColor: transparent;\n --pf-c-notification-badge--m-unread--Color: var(--pf-global--Color--light-100);\n --pf-c-notification-badge--m-unread--after--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-notification-badge--m-unread--hover--after--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-notification-badge--m-attention--Color: var(--pf-global--Color--light-100);\n --pf-c-notification-badge--m-attention--after--BackgroundColor: var(--pf-global--danger-color--100);\n --pf-c-notification-badge--m-attention--hover--after--BackgroundColor: var(--pf-global--danger-color--200);\n --pf-c-notification-badge__count--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-notification-badge--pf-icon-attention-bell--LineHeight: var(--pf-global--LineHeight--sm);\n position: relative;\n display: inline-block;\n padding: var(--pf-c-notification-badge--PaddingTop) var(--pf-c-notification-badge--PaddingRight) var(--pf-c-notification-badge--PaddingBottom) var(--pf-c-notification-badge--PaddingLeft);\n margin: var(--pf-c-notification-badge--MarginTop) var(--pf-c-notification-badge--MarginRight) var(--pf-c-notification-badge--MarginBottom) var(--pf-c-notification-badge--MarginLeft);\n background-color: var(--pf-c-notification-badge--after--BackgroundColor);\n border-radius: var(--pf-c-notification-badge--after--BorderRadius); }\n .pf-c-notification-badge::before {\n position: absolute;\n top: var(--pf-c-notification-badge--after--Top);\n right: var(--pf-c-notification-badge--after--Right);\n bottom: 0;\n left: 0;\n width: var(--pf-c-notification-badge--after--Width);\n height: var(--pf-c-notification-badge--after--Height);\n content: \"\";\n border: var(--pf-c-notification-badge--after--BorderWidth) solid var(--pf-c-notification-badge--after--BorderColor);\n border-radius: var(--pf-c-notification-badge--after--BorderRadius);\n transform: translate(var(--pf-c-notification-badge--after--TranslateX), var(--pf-c-notification-badge--after--TranslateY)); }\n .pf-c-notification-badge > i {\n width: var(--pf-c-notification-badge__i--Width);\n height: var(--pf-c-notification-badge__i--Height); }\n .pf-c-notification-badge > * {\n position: relative; }\n .pf-c-notification-badge .pf-icon-attention-bell,\n .pf-c-notification-badge .pf-icon-bell {\n display: inline-block;\n line-height: var(--pf-c-notification-badge--pf-icon-attention-bell--LineHeight); }\n .pf-c-notification-badge .pf-icon-attention-bell::before,\n .pf-c-notification-badge .pf-icon-bell::before {\n vertical-align: bottom; }\n .pf-c-notification-badge.pf-m-read {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-notification-badge--m-read--after--BackgroundColor);\n --pf-c-notification-badge--after--BorderColor: var(--pf-c-notification-badge--m-read--after--BorderColor); }\n .pf-c-notification-badge.pf-m-unread {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-notification-badge--m-unread--after--BackgroundColor);\n color: var(--pf-c-notification-badge--m-unread--Color); }\n .pf-c-notification-badge.pf-m-unread:hover {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-notification-badge--m-unread--hover--after--BackgroundColor); }\n .pf-c-notification-badge.pf-m-attention {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-notification-badge--m-attention--after--BackgroundColor);\n color: var(--pf-c-notification-badge--m-attention--Color); }\n .pf-c-notification-badge.pf-m-attention:hover {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-notification-badge--m-attention--hover--after--BackgroundColor); }\n\n.pf-c-notification-badge__count {\n margin-left: var(--pf-c-notification-badge__count--MarginLeft); }\n\n.pf-c-notification-drawer {\n --pf-c-notification-drawer--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-notification-drawer__header--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__header--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__header--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__header--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__header--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-notification-drawer__header--BoxShadow: var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-notification-drawer__header--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-notification-drawer__header-title--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-notification-drawer__header-status--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__body--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-notification-drawer__list-item--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__list-item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__list-item--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__list-item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__list-item--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-notification-drawer__list-item--BoxShadow: inset var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-notification-drawer__list-item--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-notification-drawer__list-item--BorderBottomColor: transparent;\n --pf-c-notification-drawer__list-item--OutlineOffset: -0.25rem;\n --pf-c-notification-drawer__list-item--before--Width: var(--pf-global--BorderWidth--lg);\n --pf-c-notification-drawer__list-item--before--Top: 0;\n --pf-c-notification-drawer__list-item--before--Bottom: calc(var(--pf-c-notification-drawer__list-item--BorderBottomWidth) * -1);\n --pf-c-notification-drawer__list-item--m-info__list-item-header-icon--Color: var(--pf-global--info-color--100);\n --pf-c-notification-drawer__list-item--m-info__list-item--before--BackgroundColor: var(--pf-global--info-color--100);\n --pf-c-notification-drawer__list-item--m-warning__list-item-header-icon--Color: var(--pf-global--warning-color--100);\n --pf-c-notification-drawer__list-item--m-warning__list-item--before--BackgroundColor: var(--pf-global--warning-color--100);\n --pf-c-notification-drawer__list-item--m-danger__list-item-header-icon--Color: var(--pf-global--danger-color--100);\n --pf-c-notification-drawer__list-item--m-danger__list-item--before--BackgroundColor: var(--pf-global--danger-color--100);\n --pf-c-notification-drawer__list-item--m-success__list-item-header-icon--Color: var(--pf-global--success-color--100);\n --pf-c-notification-drawer__list-item--m-success__list-item--before--BackgroundColor: var(--pf-global--success-color--100);\n --pf-c-notification-drawer__list-item--m-default__list-item-header-icon--Color: var(--pf-global--default-color--200);\n --pf-c-notification-drawer__list-item--m-default__list-item--before--BackgroundColor: var(--pf-global--default-color--200);\n --pf-c-notification-drawer__list-item--m-read--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-notification-drawer__list-item--m-read--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-notification-drawer__list-item--m-read--before--Top: calc(var(--pf-c-notification-drawer__list-item--BorderBottomWidth) * -1);\n --pf-c-notification-drawer__list-item--m-read--before--Bottom: 0;\n --pf-c-notification-drawer__list-item--m-read--before--BackgroundColor: transparent;\n --pf-c-notification-drawer__list-item--list-item--m-read--before--Top: 0;\n --pf-c-notification-drawer__list-item--list-item--m-read--BoxShadow: inset var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-notification-drawer__list-item--m-hoverable--hover--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-notification-drawer__list-item--m-hoverable--hover--BoxShadow: var(--pf-global--BoxShadow--md-top), var(--pf-global--BoxShadow--md-bottom);\n --pf-c-notification-drawer__list-item-header--MarginBottom: var(--pf-global--spacer--xs);\n --pf-c-notification-drawer__list-item-header-icon--Color: inherit;\n --pf-c-notification-drawer__list-item-header-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-notification-drawer__list-item-header-title--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-notification-drawer__list-item-header-title--max-lines: 1;\n --pf-c-notification-drawer__list-item--m-read__list-item-header-title--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-notification-drawer__list-item-description--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-notification-drawer__list-item-timestamp--Color: var(--pf-global--Color--200);\n --pf-c-notification-drawer__list-item-timestamp--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-notification-drawer__group--m-expanded--group--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-notification-drawer__group--m-expanded--group--BorderTopColor: var(--pf-global--BorderColor--100);\n --pf-c-notification-drawer__group--m-expanded--MinHeight: 0;\n --pf-c-notification-drawer__group-toggle--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-notification-drawer__group-toggle--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-notification-drawer__group-toggle--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-notification-drawer__group-toggle--OutlineOffset: -0.25rem;\n --pf-c-notification-drawer__group-toggle-title--MarginRight: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle-title--max-lines: 1;\n --pf-c-notification-drawer__group-toggle-count--MarginRight: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle-icon--MarginRight: var(--pf-global--spacer--md);\n --pf-c-notification-drawer__group-toggle-icon--Color: var(--pf-global--Color--200);\n --pf-c-notification-drawer__group-toggle-icon--Transition: .2s ease-in 0s;\n --pf-c-notification-drawer__group--m-expanded__group-toggle-icon--Rotate: 90deg;\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--pf-c-notification-drawer--BackgroundColor); }\n\n.pf-c-notification-drawer__header {\n position: relative;\n z-index: var(--pf-c-notification-drawer__header--ZIndex);\n display: flex;\n flex-shrink: 0;\n align-items: baseline;\n padding: var(--pf-c-notification-drawer__header--PaddingTop) var(--pf-c-notification-drawer__header--PaddingRight) var(--pf-c-notification-drawer__header--PaddingBottom) var(--pf-c-notification-drawer__header--PaddingLeft);\n background-color: var(--pf-c-notification-drawer__header--BackgroundColor);\n box-shadow: var(--pf-c-notification-drawer__header--BoxShadow); }\n\n.pf-c-notification-drawer__header-title {\n font-size: var(--pf-c-notification-drawer__header-title--FontSize); }\n\n.pf-c-notification-drawer__header-status {\n margin-left: var(--pf-c-notification-drawer__header-status--MarginLeft); }\n\n.pf-c-notification-drawer__header-action {\n display: flex;\n align-items: center;\n margin-left: auto; }\n\n.pf-c-notification-drawer__body {\n overflow-y: auto;\n box-shadow: var(--pf-c-notification-drawer__body--ZIndex); }\n\n.pf-c-notification-drawer__list-item {\n position: relative;\n display: grid;\n grid-template-columns: 1fr auto;\n padding: var(--pf-c-notification-drawer__list-item--PaddingTop) var(--pf-c-notification-drawer__list-item--PaddingRight) var(--pf-c-notification-drawer__list-item--PaddingBottom) var(--pf-c-notification-drawer__list-item--PaddingLeft);\n background-color: var(--pf-c-notification-drawer__list-item--BackgroundColor);\n border-bottom: var(--pf-c-notification-drawer__list-item--BorderBottomWidth) solid var(--pf-c-notification-drawer__list-item--BorderBottomColor);\n outline-offset: var(--pf-c-notification-drawer__list-item--OutlineOffset);\n box-shadow: var(--pf-c-notification-drawer__list-item--BoxShadow); }\n .pf-c-notification-drawer__list-item.pf-m-read, .pf-c-notification-drawer__list-item:first-child {\n --pf-c-notification-drawer__list-item--BoxShadow: none; }\n .pf-c-notification-drawer__list-item:not(.pf-m-read) + .pf-c-notification-drawer__list-item.pf-m-read {\n --pf-c-notification-drawer__list-item--BoxShadow: var(--pf-c-notification-drawer__list-item--list-item--m-read--BoxShadow);\n --pf-c-notification-drawer__list-item--before--Top: var(--pf-c-notification-drawer__list-item--list-item--m-read--before--Top); }\n .pf-c-notification-drawer__list-item::before {\n position: absolute;\n top: var(--pf-c-notification-drawer__list-item--before--Top);\n bottom: var(--pf-c-notification-drawer__list-item--before--Bottom);\n width: var(--pf-c-notification-drawer__list-item--before--Width);\n content: \"\";\n background-color: var(--pf-c-notification-drawer__list-item--before--BackgroundColor); }\n .pf-c-notification-drawer__list-item.pf-m-info {\n --pf-c-notification-drawer__list-item--before--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-info__list-item--before--BackgroundColor);\n --pf-c-notification-drawer__list-item-header-icon--Color: var(--pf-c-notification-drawer__list-item--m-info__list-item-header-icon--Color); }\n .pf-c-notification-drawer__list-item.pf-m-warning {\n --pf-c-notification-drawer__list-item--before--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-warning__list-item--before--BackgroundColor);\n --pf-c-notification-drawer__list-item-header-icon--Color: var(--pf-c-notification-drawer__list-item--m-warning__list-item-header-icon--Color); }\n .pf-c-notification-drawer__list-item.pf-m-danger {\n --pf-c-notification-drawer__list-item--before--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-danger__list-item--before--BackgroundColor);\n --pf-c-notification-drawer__list-item-header-icon--Color: var(--pf-c-notification-drawer__list-item--m-danger__list-item-header-icon--Color); }\n .pf-c-notification-drawer__list-item.pf-m-success {\n --pf-c-notification-drawer__list-item--before--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-success__list-item--before--BackgroundColor);\n --pf-c-notification-drawer__list-item-header-icon--Color: var(--pf-c-notification-drawer__list-item--m-success__list-item-header-icon--Color); }\n .pf-c-notification-drawer__list-item.pf-m-default {\n --pf-c-notification-drawer__list-item--before--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-default__list-item--before--BackgroundColor);\n --pf-c-notification-drawer__list-item-header-icon--Color: var(--pf-c-notification-drawer__list-item--m-default__list-item-header-icon--Color); }\n .pf-c-notification-drawer__list-item.pf-m-read {\n --pf-c-notification-drawer__list-item--BorderBottomColor: var(--pf-c-notification-drawer__list-item--m-read--BorderBottomColor);\n --pf-c-notification-drawer__list-item--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-read--BackgroundColor);\n --pf-c-notification-drawer__list-item--before--Top: var(--pf-c-notification-drawer__list-item--m-read--before--Top);\n --pf-c-notification-drawer__list-item--before--Bottom: var(--pf-c-notification-drawer__list-item--m-read--before--Bottom);\n --pf-c-notification-drawer__list-item--before--BackgroundColor: var(--pf-c-notification-drawer__list-item--m-read--before--BackgroundColor);\n --pf-c-notification-drawer__list-item-header-title--FontWeight: var(--pf-c-notification-drawer__list-item--m-read__list-item-header-title--FontWeight);\n position: relative; }\n .pf-c-notification-drawer__list-item.pf-m-hoverable {\n cursor: pointer; }\n .pf-c-notification-drawer__list-item.pf-m-hoverable:hover {\n z-index: var(--pf-c-notification-drawer__list-item--m-hoverable--hover--ZIndex);\n box-shadow: var(--pf-c-notification-drawer__list-item--m-hoverable--hover--BoxShadow); }\n\n.pf-c-notification-drawer__list-item-header {\n display: flex;\n align-items: baseline;\n grid-column: 1 / 2;\n grid-row: 1 / 2;\n margin-bottom: var(--pf-c-notification-drawer__list-item-header--MarginBottom); }\n\n.pf-c-notification-drawer__list-item-header-icon {\n margin-right: var(--pf-c-notification-drawer__list-item-header-icon--MarginRight);\n color: var(--pf-c-notification-drawer__list-item-header-icon--Color); }\n\n.pf-c-notification-drawer__list-item-header-title {\n font-weight: var(--pf-c-notification-drawer__list-item-header-title--FontWeight);\n word-break: break-word; }\n .pf-c-notification-drawer__list-item-header-title.pf-m-truncate {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: var(--pf-c-notification-drawer__list-item-header-title--max-lines);\n overflow: hidden; }\n\n.pf-c-notification-drawer__list-item-action {\n grid-column: 2 / 3;\n grid-row: 1 / 3; }\n\n.pf-c-notification-drawer__list-item-description {\n grid-row: 2 / 3;\n grid-column: 1 / 2;\n margin-bottom: var(--pf-c-notification-drawer__list-item-description--MarginBottom);\n word-break: break-word; }\n\n.pf-c-notification-drawer__list-item-timestamp {\n grid-row: 3 / 4;\n grid-column: 1 / 2;\n font-size: var(--pf-c-notification-drawer__list-item-timestamp--FontSize);\n color: var(--pf-c-notification-drawer__list-item-timestamp--Color); }\n\n.pf-c-notification-drawer__group-list {\n display: flex;\n flex-direction: column; }\n\n.pf-c-notification-drawer__group.pf-m-expanded {\n min-height: var(--pf-c-notification-drawer__group--m-expanded--MinHeight); }\n .pf-c-notification-drawer__group.pf-m-expanded + .pf-c-notification-drawer__group {\n border-top: var(--pf-c-notification-drawer__group--m-expanded--group--BorderTopWidth) solid var(--pf-c-notification-drawer__group--m-expanded--group--BorderTopColor); }\n\n.pf-c-notification-drawer__group .pf-c-notification-drawer__list-item:last-child {\n --pf-c-notification-drawer__list-item--BorderBottomWidth: 0;\n --pf-c-notification-drawer__list-item--before--Bottom: 0; }\n\n.pf-c-notification-drawer__group-toggle {\n display: flex;\n align-items: baseline;\n width: 100%;\n padding: var(--pf-c-notification-drawer__group-toggle--PaddingTop) var(--pf-c-notification-drawer__group-toggle--PaddingRight) var(--pf-c-notification-drawer__group-toggle--PaddingBottom) var(--pf-c-notification-drawer__group-toggle--PaddingLeft);\n background-color: var(--pf-c-notification-drawer__group-toggle--BackgroundColor);\n border: solid var(--pf-c-notification-drawer__group-toggle--BorderColor);\n border-width: 0 0 var(--pf-c-notification-drawer__group-toggle--BorderBottomWidth) 0;\n outline-offset: var(--pf-c-notification-drawer__group-toggle--OutlineOffset); }\n\n.pf-c-notification-drawer__group-toggle-title {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: var(--pf-c-notification-drawer__group-toggle-title--max-lines);\n overflow: hidden;\n margin-right: var(--pf-c-notification-drawer__group-toggle-title--MarginRight);\n text-align: left;\n word-break: break-word; }\n\n.pf-c-notification-drawer__group-toggle-count {\n margin-right: var(--pf-c-notification-drawer__group-toggle-count--MarginRight);\n margin-left: auto; }\n\n.pf-c-notification-drawer__group-toggle-icon {\n margin-right: var(--pf-c-notification-drawer__group-toggle-icon--MarginRight);\n color: var(--pf-c-notification-drawer__group-toggle-icon--Color);\n transition: var(--pf-c-notification-drawer__group-toggle-icon--Transition); }\n .pf-c-notification-drawer__group.pf-m-expanded .pf-c-notification-drawer__group-toggle-icon {\n transform: rotate(var(--pf-c-notification-drawer__group--m-expanded__group-toggle-icon--Rotate)); }\n\n.pf-c-options-menu {\n --pf-c-options-menu__toggle--BackgroundColor: transparent;\n --pf-c-options-menu__toggle--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-options-menu__toggle--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-options-menu__toggle--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-options-menu__toggle--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-options-menu__toggle--MinWidth: var(--pf-global--target-size--MinWidth);\n --pf-c-options-menu__toggle--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-options-menu__toggle--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-options-menu__toggle--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-options-menu__toggle--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-options-menu__toggle--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-options-menu__toggle--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-options-menu__toggle--Color: var(--pf-global--Color--100);\n --pf-c-options-menu__toggle--hover--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-options-menu__toggle--active--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-options-menu__toggle--active--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-options-menu__toggle--focus--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-options-menu__toggle--focus--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-options-menu__toggle--expanded--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-options-menu__toggle--expanded--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-options-menu__toggle--disabled--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-options-menu__toggle--m-plain--Color: var(--pf-global--Color--200);\n --pf-c-options-menu__toggle--m-plain--hover--Color: var(--pf-global--Color--100);\n --pf-c-options-menu__toggle--m-plain--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-options-menu__toggle-icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-options-menu__toggle-icon--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-options-menu--m-top--m-expanded__toggle-icon--Rotate: 180deg;\n --pf-c-options-menu__toggle-button--BackgroundColor: transparent;\n --pf-c-options-menu__toggle-button--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-options-menu__toggle-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-options-menu__toggle-button--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-options-menu__toggle-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-options-menu__menu--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-options-menu__menu--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-options-menu__menu--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-options-menu__menu--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-options-menu__menu--Top: calc(100% + var(--pf-global--spacer--xs));\n --pf-c-options-menu__menu--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-options-menu--m-top__menu--Top: 0;\n --pf-c-options-menu--m-top__menu--TranslateY: calc(-100% - var(--pf-global--spacer--xs));\n --pf-c-options-menu__menu-item--BackgroundColor: transparent;\n --pf-c-options-menu__menu-item--Color: var(--pf-global--Color--100);\n --pf-c-options-menu__menu-item--FontSize: var(--pf-global--FontSize--md);\n --pf-c-options-menu__menu-item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-options-menu__menu-item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-options-menu__menu-item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-options-menu__menu-item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-options-menu__menu-item--disabled--Color: var(--pf-global--Color--dark-200);\n --pf-c-options-menu__menu-item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-options-menu__menu-item--disabled--BackgroundColor: transparent;\n --pf-c-options-menu__menu-item-icon--Color: var(--pf-global--active-color--100);\n --pf-c-options-menu__menu-item-icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-options-menu__menu-item-icon--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-options-menu__group--group--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-options-menu__group-title--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-options-menu__group-title--PaddingRight: var(--pf-c-options-menu__menu-item--PaddingRight);\n --pf-c-options-menu__group-title--PaddingBottom: var(--pf-c-options-menu__menu-item--PaddingBottom);\n --pf-c-options-menu__group-title--PaddingLeft: var(--pf-c-options-menu__menu-item--PaddingLeft);\n --pf-c-options-menu__group-title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-options-menu__group-title--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-options-menu__group-title--Color: var(--pf-global--Color--dark-200);\n --pf-c-options-menu--c-divider--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-options-menu--c-divider--MarginBottom: var(--pf-global--spacer--sm);\n position: relative;\n display: inline-block;\n max-width: 100%; }\n .pf-c-options-menu .pf-c-divider {\n margin-top: var(--pf-c-options-menu--c-divider--MarginTop);\n margin-bottom: var(--pf-c-options-menu--c-divider--MarginBottom); }\n .pf-c-options-menu .pf-c-divider:last-child {\n --pf-c-options-menu--c-divider--MarginBottom: 0; }\n\n.pf-c-options-menu__toggle:not(.pf-m-plain)::before,\n.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-options-menu__toggle--BorderWidth) solid;\n border-color: var(--pf-c-options-menu__toggle--BorderTopColor) var(--pf-c-options-menu__toggle--BorderRightColor) var(--pf-c-options-menu__toggle--BorderBottomColor) var(--pf-c-options-menu__toggle--BorderLeftColor); }\n\n.pf-c-options-menu__toggle:not(.pf-m-plain):hover::before,\n.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:hover::before {\n --pf-c-options-menu__toggle--BorderBottomColor: var(--pf-c-options-menu__toggle--hover--BorderBottomColor); }\n\n.pf-c-options-menu__toggle:not(.pf-m-plain):active::before, .pf-c-options-menu__toggle:not(.pf-m-plain).pf-m-active::before,\n.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:active::before,\n.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button.pf-m-active::before {\n --pf-c-options-menu__toggle--BorderBottomColor: var(--pf-c-options-menu__toggle--active--BorderBottomColor);\n border-bottom-width: var(--pf-c-options-menu__toggle--active--BorderBottomWidth); }\n\n.pf-c-options-menu__toggle:not(.pf-m-plain):focus::before,\n.pf-c-options-menu.pf-m-text:not(.pf-m-plain) .pf-c-options-menu__toggle-button:focus::before {\n --pf-c-options-menu__toggle--BorderBottomColor: var(--pf-c-options-menu__toggle--focus--BorderBottomColor);\n border-bottom-width: var(--pf-c-options-menu__toggle--focus--BorderBottomWidth); }\n\n.pf-c-options-menu__toggle {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-width: var(--pf-c-options-menu__toggle--MinWidth);\n max-width: 100%;\n padding-left: var(--pf-c-options-menu__toggle--PaddingLeft);\n line-height: var(--pf-c-options-menu__toggle--LineHeight);\n color: var(--pf-c-options-menu__toggle--Color);\n background-color: var(--pf-c-options-menu__toggle--BackgroundColor);\n border: none; }\n .pf-c-options-menu__toggle:not(.pf-m-text) {\n padding-top: var(--pf-c-options-menu__toggle--PaddingTop);\n padding-right: var(--pf-c-options-menu__toggle--PaddingRight);\n padding-bottom: var(--pf-c-options-menu__toggle--PaddingBottom); }\n .pf-c-options-menu.pf-m-expanded > .pf-c-options-menu__toggle::before {\n --pf-c-options-menu__toggle--BorderBottomColor: var(--pf-c-options-menu__toggle--expanded--BorderBottomColor);\n border-bottom-width: var(--pf-c-options-menu__toggle--expanded--BorderBottomWidth); }\n .pf-c-options-menu__toggle.pf-m-plain:not(.pf-m-text) {\n justify-content: center;\n color: var(--pf-c-options-menu__toggle--m-plain--Color); }\n .pf-c-options-menu__toggle.pf-m-plain .pf-c-options-menu__toggle-button-icon {\n line-height: var(--pf-c-options-menu__toggle--LineHeight); }\n .pf-c-options-menu__toggle.pf-m-plain:hover, .pf-c-options-menu__toggle.pf-m-plain:active, .pf-c-options-menu__toggle.pf-m-plain.pf-m-active, .pf-c-options-menu__toggle.pf-m-plain:focus,\n .pf-c-options-menu.pf-m-expanded > .pf-c-options-menu__toggle.pf-m-plain {\n --pf-c-options-menu__toggle--m-plain--Color: var(--pf-c-options-menu__toggle--m-plain--hover--Color); }\n .pf-c-options-menu__toggle.pf-m-plain.pf-m-disabled, .pf-c-options-menu__toggle.pf-m-plain:disabled {\n --pf-c-options-menu__toggle--m-plain--Color: var(--pf-c-options-menu__toggle--m-plain--disabled--Color); }\n .pf-c-options-menu__toggle.pf-m-disabled, .pf-c-options-menu__toggle:disabled {\n pointer-events: none; }\n .pf-c-options-menu__toggle.pf-m-disabled:not(.pf-m-plain), .pf-c-options-menu__toggle.pf-m-disabled.pf-m-text, .pf-c-options-menu__toggle:disabled:not(.pf-m-plain), .pf-c-options-menu__toggle:disabled.pf-m-text {\n --pf-c-options-menu__toggle--BackgroundColor: var(--pf-c-options-menu__toggle--disabled--BackgroundColor); }\n .pf-c-options-menu__toggle.pf-m-disabled::before, .pf-c-options-menu__toggle:disabled::before {\n border: 0; }\n\n.pf-c-options-menu__toggle-button-icon {\n position: relative; }\n\n.pf-c-options-menu__toggle-button {\n padding: var(--pf-c-options-menu__toggle-button--PaddingTop) var(--pf-c-options-menu__toggle-button--PaddingRight) var(--pf-c-options-menu__toggle-button--PaddingBottom) var(--pf-c-options-menu__toggle-button--PaddingLeft);\n background-color: var(--pf-c-options-menu__toggle-button--BackgroundColor);\n border: 0; }\n\n.pf-c-options-menu__toggle-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n\n.pf-c-options-menu__toggle-icon {\n margin-right: var(--pf-c-options-menu__toggle-icon--MarginRight);\n margin-left: var(--pf-c-options-menu__toggle-icon--MarginLeft); }\n .pf-c-options-menu.pf-m-top.pf-m-expanded .pf-c-options-menu__toggle-icon {\n transform: rotate(var(--pf-c-options-menu--m-top--m-expanded__toggle-icon--Rotate)); }\n\n.pf-c-options-menu__menu {\n position: absolute;\n top: var(--pf-c-options-menu__menu--Top);\n z-index: var(--pf-c-options-menu__menu--ZIndex);\n min-width: 100%;\n padding-top: var(--pf-c-options-menu__menu--PaddingTop);\n padding-bottom: var(--pf-c-options-menu__menu--PaddingBottom);\n background-color: var(--pf-c-options-menu__menu--BackgroundColor);\n background-clip: padding-box;\n box-shadow: var(--pf-c-options-menu__menu--BoxShadow); }\n .pf-c-options-menu__menu.pf-m-align-right {\n right: 0; }\n .pf-c-options-menu.pf-m-top .pf-c-options-menu__menu {\n --pf-c-options-menu__menu--Top: var(--pf-c-options-menu--m-top__menu--Top);\n transform: translateY(var(--pf-c-options-menu--m-top__menu--TranslateY)); }\n\n.pf-c-options-menu__menu-item {\n display: flex;\n align-items: baseline;\n width: 100%;\n padding: var(--pf-c-options-menu__menu-item--PaddingTop) var(--pf-c-options-menu__menu-item--PaddingRight) var(--pf-c-options-menu__menu-item--PaddingBottom) var(--pf-c-options-menu__menu-item--PaddingLeft);\n font-size: var(--pf-c-options-menu__menu-item--FontSize);\n color: var(--pf-c-options-menu__menu-item--Color);\n white-space: nowrap;\n background-color: var(--pf-c-options-menu__menu-item--BackgroundColor);\n border: none; }\n .pf-c-options-menu__menu-item:hover, .pf-c-options-menu__menu-item:focus {\n text-decoration: none;\n background-color: var(--pf-c-options-menu__menu-item--hover--BackgroundColor); }\n .pf-c-options-menu__menu-item:disabled, .pf-c-options-menu__menu-item.pf-m-disabled {\n color: var(--pf-c-options-menu__menu-item--disabled--Color);\n pointer-events: none;\n background-color: var(--pf-c-options-menu__menu-item--disabled--BackgroundColor); }\n\n.pf-c-options-menu__menu-item-icon {\n align-self: center;\n width: auto;\n padding-left: var(--pf-c-options-menu__menu-item-icon--PaddingLeft);\n margin-left: auto;\n font-size: var(--pf-c-options-menu__menu-item-icon--FontSize);\n color: var(--pf-c-options-menu__menu-item-icon--Color); }\n\n.pf-c-options-menu__group + .pf-c-options-menu__group {\n padding-top: var(--pf-c-options-menu__group--group--PaddingTop); }\n\n.pf-c-options-menu__group-title {\n padding-top: var(--pf-c-options-menu__group-title--PaddingTop);\n padding-right: var(--pf-c-options-menu__group-title--PaddingRight);\n padding-bottom: var(--pf-c-options-menu__group-title--PaddingBottom);\n padding-left: var(--pf-c-options-menu__group-title--PaddingLeft);\n font-size: var(--pf-c-options-menu__group-title--FontSize);\n font-weight: var(--pf-c-options-menu__group-title--FontWeight);\n color: var(--pf-c-options-menu__group-title--Color); }\n\n.pf-c-overflow-menu {\n --pf-c-overflow-menu--spacer--base: var(--pf-global--spacer--md);\n --pf-c-overflow-menu--spacer: var(--pf-global--spacer--sm);\n --pf-c-overflow-menu__group--spacer: var(--pf-c-overflow-menu--spacer--base);\n --pf-c-overflow-menu__item--spacer: var(--pf-c-overflow-menu--spacer--base);\n --pf-c-overflow-menu--c-divider--m-vertical--spacer: var(--pf-c-overflow-menu--spacer--base);\n --pf-c-overflow-menu__group--m-button-group--spacer: var(--pf-c-overflow-menu--spacer--base);\n --pf-c-overflow-menu__group--m-button-group--space-items: var(--pf-global--spacer--sm);\n --pf-c-overflow-menu__group--m-icon-button-group--spacer: var(--pf-c-overflow-menu--spacer--base);\n --pf-c-overflow-menu__group--m-icon-button-group--space-items: 0;\n display: inline-flex;\n align-items: center; }\n\n.pf-c-overflow-menu__content {\n display: flex;\n align-items: center; }\n\n.pf-c-overflow-menu__group {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu__group--spacer);\n display: flex;\n align-items: center; }\n .pf-c-overflow-menu__group.pf-m-button-group {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu__group--m-button-group--spacer); }\n .pf-c-overflow-menu__group.pf-m-button-group > * {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu__group--m-button-group--space-items); }\n .pf-c-overflow-menu__group.pf-m-icon-button-group {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu__group--m-icon-button-group--spacer); }\n .pf-c-overflow-menu__group.pf-m-icon-button-group > * {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu__group--m-icon-button-group--space-items); }\n\n.pf-c-overflow-menu__item {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu__item--spacer); }\n\n.pf-c-overflow-menu__content,\n.pf-c-overflow-menu__control,\n.pf-c-overflow-menu__group,\n.pf-c-overflow-menu__item {\n margin-right: var(--pf-c-overflow-menu--spacer); }\n .pf-c-overflow-menu__content:last-child,\n .pf-c-overflow-menu__control:last-child,\n .pf-c-overflow-menu__group:last-child,\n .pf-c-overflow-menu__item:last-child {\n --pf-c-overflow-menu--spacer: 0; }\n\n.pf-c-overflow-menu > .pf-c-divider,\n.pf-c-overflow-menu__group > .pf-c-divider {\n --pf-c-overflow-menu--spacer: var(--pf-c-overflow-menu--c-divider--m-vertical--spacer); }\n\n.pf-c-overflow-menu > .pf-c-divider.pf-m-vertical,\n.pf-c-overflow-menu__group > .pf-c-divider.pf-m-vertical {\n margin-right: var(--pf-c-overflow-menu--spacer); }\n .pf-c-overflow-menu > .pf-c-divider.pf-m-vertical:last-child,\n .pf-c-overflow-menu__group > .pf-c-divider.pf-m-vertical:last-child {\n --pf-c-overflow-menu--spacer: 0; }\n\n.pf-c-page {\n --pf-c-page--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-page__header--BackgroundColor: var(--pf-global--BackgroundColor--dark-100);\n --pf-c-page__header--ZIndex: var(--pf-global--ZIndex--md);\n --pf-c-page__header--MinHeight: 4.75rem;\n --pf-c-page__header-brand--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-page__header-brand--xl--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-page__header-brand--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-page__header-sidebar-toggle__c-button--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-page__header-sidebar-toggle__c-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-page__header-sidebar-toggle__c-button--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-page__header-sidebar-toggle__c-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-page__header-sidebar-toggle__c-button--MarginRight: var(--pf-global--spacer--md);\n --pf-c-page__header-sidebar-toggle__c-button--MarginLeft: calc(var(--pf-c-page__header-sidebar-toggle__c-button--PaddingLeft) * -1);\n --pf-c-page__header-sidebar-toggle__c-button--FontSize: var(--pf-global--FontSize--2xl);\n --pf-c-page__header-brand-link--c-brand--MaxHeight: 3.75rem;\n --pf-c-page__header-nav--BackgroundColor: var(--pf-global--BackgroundColor--dark-300);\n --pf-c-page__header-nav--xl--BackgroundColor: transparent;\n --pf-c-page__header-nav--xl--PaddingRight: var(--pf-global--spacer--xl);\n --pf-c-page__header-nav--xl--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-page__header-tools--MarginRight: var(--pf-global--spacer--md);\n --pf-c-page__header-tools--xl--MarginRight: var(--pf-global--spacer--lg);\n --pf-c-page__header-tools--c-avatar--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-page__header-tools-group--MarginLeft: var(--pf-global--spacer--xl);\n --pf-c-page__header-tools-group--Display: flex;\n --pf-c-page__header-tools-item--Display: block;\n --pf-c-page__header-tools-item--c-notification-badge--hover--BackgroundColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-page__header-tools--c-button--notification-badge--m-unread--after--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-page__header-tools--c-button--notification-badge--m-attention--after--BackgroundColor: var(--pf-global--danger-color--200);\n --pf-c-page__header-tools--c-button--m-selected--notification-badge--m-unread--after--BackgroundColor: var(--pf-global--primary-color--200);\n --pf-c-page__header-tools--c-button--m-selected--notification-badge--m-attention--after--BackgroundColor: var(--pf-global--danger-color--200);\n --pf-c-page__header-tools--c-button--m-selected--before--Width: auto;\n --pf-c-page__header-tools--c-button--m-selected--before--Height: auto;\n --pf-c-page__header-tools--c-button--m-selected--before--BackgroundColor: var(--pf-global--BackgroundColor--dark-200);\n --pf-c-page__header-tools--c-button--m-selected--before--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-page__header-tools--c-button--m-selected--c-notification-badge--m-unread--after--BorderColor: transparent;\n --pf-c-page__sidebar--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-page__sidebar--Width: 80%;\n --pf-c-page__sidebar--Width: 18.125rem;\n --pf-c-page__sidebar--BackgroundColor: var(--pf-global--BackgroundColor--dark-300);\n --pf-c-page__sidebar--m-light--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-page__sidebar--BoxShadow: var(--pf-global--BoxShadow--lg-right);\n --pf-c-page__sidebar--Transition: var(--pf-global--Transition);\n --pf-c-page__sidebar--TranslateX: -100%;\n --pf-c-page__sidebar--TranslateZ: 0;\n --pf-c-page__sidebar--m-expanded--TranslateX: 0;\n --pf-c-page__sidebar--xl--TranslateX: 0;\n --pf-c-page__sidebar-body--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-page__sidebar-body--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-page__main--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-page__main-section--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-page__main-section--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-page__main-section--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-page__main-section--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-page__main-section--xl--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-page__main-section--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-page__main-section--xl--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-page__main-section--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-page__main-breadcrumb--main-section--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-page__main-section--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-page__main-section--m-light--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-page__main-section--m-dark-100--BackgroundColor: var(--pf-global--BackgroundColor--dark-transparent-100);\n --pf-c-page__main-section--m-dark-200--BackgroundColor: var(--pf-global--BackgroundColor--dark-transparent-200);\n --pf-c-page--section--m-limit-width--MaxWidth: calc(125rem - var(--pf-c-page__sidebar--Width));\n --pf-c-page--section--m-sticky-top--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-page--section--m-sticky-top--BoxShadow: var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-page--section--m-sticky-bottom--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-page--section--m-sticky-bottom--BoxShadow: var(--pf-global--BoxShadow--sm-top);\n --pf-c-page--section--m-shadow-bottom--BoxShadow: var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-page--section--m-shadow-bottom--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-page--section--m-shadow-top--BoxShadow: var(--pf-global--BoxShadow--sm-top);\n --pf-c-page--section--m-shadow-top--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-page__main-nav--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-page__main-nav--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-page__main-nav--PaddingRight: 0;\n --pf-c-page__main-nav--PaddingLeft: 0;\n --pf-c-page__main-nav--m-sticky-top--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-page__main-nav--xl--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-page__main-nav--xl--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-page__main-breadcrumb--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-page__main-breadcrumb--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-page__main-breadcrumb--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-page__main-breadcrumb--PaddingBottom: 0;\n --pf-c-page__main-breadcrumb--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-page__main-breadcrumb--m-sticky-top--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-page__main-breadcrumb--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-page__main-breadcrumb--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-page__main-wizard--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-page__main-wizard--BorderTopColor: var(--pf-global--BorderColor--100);\n --pf-c-page__main-wizard--BorderTopWidth: var(--pf-global--BorderWidth--sm);\n display: grid;\n height: 100%;\n grid-template-columns: 1fr;\n grid-template-rows: max-content 1fr;\n grid-template-areas: \"header\" \"main\";\n background-color: var(--pf-c-page--BackgroundColor); }\n @media (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__header-brand--PaddingLeft: var(--pf-c-page__header-brand--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__header-nav--BackgroundColor: var(--pf-c-page__header-nav--xl--BackgroundColor);\n --pf-c-page__header-nav--PaddingRight: var(--pf-c-page__header-nav--xl--PaddingRight);\n --pf-c-page__header-nav--PaddingLeft: var(--pf-c-page__header-nav--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__header-tools--MarginRight: var(--pf-c-page__header-tools--xl--MarginRight); } }\n @media screen and (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__sidebar--TranslateX: var(--pf-c-page__sidebar--xl--TranslateX); } }\n @media screen and (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__main-section--PaddingTop: var(--pf-c-page__main-section--xl--PaddingTop);\n --pf-c-page__main-section--PaddingRight: var(--pf-c-page__main-section--xl--PaddingRight);\n --pf-c-page__main-section--PaddingBottom: var(--pf-c-page__main-section--xl--PaddingBottom);\n --pf-c-page__main-section--PaddingLeft: var(--pf-c-page__main-section--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__main-nav--PaddingRight: var(--pf-c-page__main-nav--xl--PaddingRight);\n --pf-c-page__main-nav--PaddingLeft: var(--pf-c-page__main-nav--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-page {\n --pf-c-page__main-breadcrumb--PaddingRight: var(--pf-c-page__main-breadcrumb--xl--PaddingRight);\n --pf-c-page__main-breadcrumb--PaddingLeft: var(--pf-c-page__main-breadcrumb--xl--PaddingLeft); } }\n @media (min-width: 1200px) {\n .pf-c-page {\n grid-template-columns: max-content 1fr;\n grid-template-areas: \"header header\" \"nav main\"; } }\n\n.pf-c-page__header {\n color: var(--pf-global--Color--100);\n z-index: var(--pf-c-page__header--ZIndex);\n grid-template-columns: auto auto;\n display: grid;\n grid-area: header;\n align-items: center;\n min-width: 0;\n min-height: var(--pf-c-page__header--MinHeight);\n background-color: var(--pf-c-page__header--BackgroundColor); }\n .pf-c-page__header > * {\n display: flex;\n align-items: center; }\n @media screen and (min-width: 992px) {\n .pf-c-page__header {\n grid-template-columns: auto 1fr auto; } }\n\n.pf-c-page__header-brand {\n grid-column: 1 / 2;\n padding-left: var(--pf-c-page__header-brand--PaddingLeft); }\n @media (min-width: 1200px) {\n .pf-c-page__header-brand {\n padding-right: var(--pf-c-page__header-brand--xl--PaddingRight); } }\n\n.pf-c-page__header-brand-link {\n display: flex;\n flex: 1;\n align-items: center; }\n .pf-c-page__header-brand-link .pf-c-brand {\n max-height: var(--pf-c-page__header-brand-link--c-brand--MaxHeight); }\n\n.pf-c-page__header-brand-toggle .pf-c-button {\n padding: var(--pf-c-page__header-sidebar-toggle__c-button--PaddingTop) var(--pf-c-page__header-sidebar-toggle__c-button--PaddingRight) var(--pf-c-page__header-sidebar-toggle__c-button--PaddingBottom) var(--pf-c-page__header-sidebar-toggle__c-button--PaddingLeft);\n margin-right: var(--pf-c-page__header-sidebar-toggle__c-button--MarginRight);\n margin-left: var(--pf-c-page__header-sidebar-toggle__c-button--MarginLeft);\n font-size: var(--pf-c-page__header-sidebar-toggle__c-button--FontSize);\n line-height: 1; }\n\n.pf-c-page__header-nav {\n align-self: stretch;\n min-width: 0;\n padding-right: var(--pf-c-page__header-nav--PaddingRight);\n padding-left: var(--pf-c-page__header-nav--PaddingLeft);\n background-color: var(--pf-c-page__header-nav--BackgroundColor);\n grid-column: 1 / -1;\n grid-row: 2 / 3; }\n @media screen and (min-width: 1200px) {\n .pf-c-page__header-nav {\n grid-column: 2 / 3;\n grid-row: 1 / 2; } }\n .pf-c-page__header-nav .pf-c-nav {\n align-self: stretch; }\n\n.pf-c-page__header-tools {\n grid-column: 2 / 3;\n margin-right: var(--pf-c-page__header-tools--MarginRight);\n margin-left: auto; }\n .pf-c-page__header-tools .pf-c-avatar {\n margin-left: var(--pf-c-page__header-tools--c-avatar--MarginLeft); }\n @media screen and (min-width: 992px) {\n .pf-c-page__header-tools {\n grid-column: 3 / 4; } }\n\n.pf-c-page__header-tools-group {\n --pf-hidden-visible--visible--Display: var(--pf-c-page__header-tools-group--Display);\n align-items: center; }\n .pf-c-page__header-tools-group + .pf-c-page__header-tools-group {\n margin-left: var(--pf-c-page__header-tools-group--MarginLeft); }\n\n.pf-c-page__header-tools-item {\n --pf-hidden-visible--visible--Display: var(--pf-c-page__header-tools-item--Display); }\n .pf-c-page__header-tools-item .pf-c-notification-badge.pf-m-read:hover {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-page__header-tools-item--c-notification-badge--hover--BackgroundColor); }\n .pf-c-page__header-tools-item.pf-m-selected .pf-c-button {\n background-color: var(--pf-c-page__header-tools--c-button--m-selected--before--BackgroundColor);\n border-radius: var(--pf-c-page__header-tools--c-button--m-selected--before--BorderRadius); }\n .pf-c-page__header-tools-item.pf-m-selected .pf-c-button::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n width: var(--pf-c-page__header-tools--c-button--m-selected--before--Width);\n height: var(--pf-c-page__header-tools--c-button--m-selected--before--Height);\n content: \"\"; }\n .pf-c-page__header-tools-item.pf-m-selected .pf-c-button .pf-c-notification-badge.pf-m-unread {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-page__header-tools--c-button--m-selected--notification-badge--m-unread--after--BackgroundColor); }\n .pf-c-page__header-tools-item.pf-m-selected .pf-c-button .pf-c-notification-badge.pf-m-unread::after {\n border-color: var(--pf-c-page__header-tools--c-button--m-selected--c-notification-badge--m-unread--after--BorderColor); }\n .pf-c-page__header-tools-item.pf-m-selected .pf-c-button .pf-c-notification-badge.pf-m-attention {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-global--danger-color--200); }\n .pf-c-page__header-tools-item .pf-c-button:focus .pf-c-notification-badge.pf-m-unread {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-page__header-tools--c-button--notification-badge--m-unread--after--BackgroundColor); }\n .pf-c-page__header-tools-item .pf-c-button:focus .pf-c-notification-badge.pf-m-attention {\n --pf-c-notification-badge--after--BackgroundColor: var(--pf-c-page__header-tools--c-button--notification-badge--m-attention--after--BackgroundColor); }\n\n.pf-c-page__sidebar {\n grid-area: nav;\n grid-row-start: 2;\n grid-column-start: 1;\n z-index: var(--pf-c-page__sidebar--ZIndex);\n width: var(--pf-c-page__sidebar--Width);\n overflow-x: hidden;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n background-color: var(--pf-c-page__sidebar--BackgroundColor);\n transition: var(--pf-c-page__sidebar--Transition);\n transform: translateX(var(--pf-c-page__sidebar--TranslateX)) translateZ(var(--pf-c-page__sidebar--TranslateZ)); }\n @media screen and (min-width: 1200px) {\n .pf-c-page__sidebar {\n box-shadow: var(--pf-c-page__sidebar--BoxShadow); } }\n .pf-c-page__sidebar.pf-m-expanded {\n --pf-c-page__sidebar--TranslateX: var(--pf-c-page__sidebar--m-expanded--TranslateX);\n box-shadow: var(--pf-c-page__sidebar--BoxShadow); }\n .pf-c-page__sidebar.pf-m-collapsed {\n max-width: 0;\n overflow: hidden; }\n .pf-c-page__sidebar.pf-m-light {\n color: var(--pf-global--Color--100);\n --pf-c-page__sidebar--BackgroundColor: var(--pf-c-page__sidebar--m-light--BackgroundColor); }\n\n.pf-c-page__sidebar-body {\n padding-top: var(--pf-c-page__sidebar-body--PaddingTop);\n padding-bottom: var(--pf-c-page__sidebar-body--PaddingBottom); }\n\n.pf-c-page__main-nav.pf-m-limit-width,\n.pf-c-page__main-breadcrumb.pf-m-limit-width,\n.pf-c-page__main-section.pf-m-limit-width,\n.pf-c-page__main-wizard.pf-m-limit-width {\n display: flex;\n flex-direction: column;\n padding: 0; }\n .pf-c-page__main-nav.pf-m-limit-width > .pf-c-page__main-body,\n .pf-c-page__main-breadcrumb.pf-m-limit-width > .pf-c-page__main-body,\n .pf-c-page__main-section.pf-m-limit-width > .pf-c-page__main-body,\n .pf-c-page__main-wizard.pf-m-limit-width > .pf-c-page__main-body {\n flex: 1;\n max-width: var(--pf-c-page--section--m-limit-width--MaxWidth); }\n\n.pf-c-page__main-nav,\n.pf-c-page__main-breadcrumb,\n.pf-c-page__main-section,\n.pf-c-page__main-wizard,\n.pf-c-page__main-group {\n flex-shrink: 0; }\n .pf-c-page__main-nav.pf-m-sticky-top,\n .pf-c-page__main-breadcrumb.pf-m-sticky-top,\n .pf-c-page__main-section.pf-m-sticky-top,\n .pf-c-page__main-wizard.pf-m-sticky-top,\n .pf-c-page__main-group.pf-m-sticky-top {\n position: sticky;\n top: 0;\n z-index: var(--pf-c-page--section--m-sticky-top--ZIndex);\n box-shadow: var(--pf-c-page--section--m-sticky-top--BoxShadow); }\n .pf-c-page__main-nav.pf-m-sticky-bottom,\n .pf-c-page__main-breadcrumb.pf-m-sticky-bottom,\n .pf-c-page__main-section.pf-m-sticky-bottom,\n .pf-c-page__main-wizard.pf-m-sticky-bottom,\n .pf-c-page__main-group.pf-m-sticky-bottom {\n position: sticky;\n bottom: 0;\n z-index: var(--pf-c-page--section--m-sticky-bottom--ZIndex);\n box-shadow: var(--pf-c-page--section--m-sticky-bottom--BoxShadow); }\n .pf-c-page__main-nav.pf-m-overflow-scroll,\n .pf-c-page__main-breadcrumb.pf-m-overflow-scroll,\n .pf-c-page__main-section.pf-m-overflow-scroll,\n .pf-c-page__main-wizard.pf-m-overflow-scroll,\n .pf-c-page__main-group.pf-m-overflow-scroll {\n position: relative;\n flex-shrink: 1;\n overflow: auto; }\n .pf-c-page__main-nav.pf-m-shadow-bottom,\n .pf-c-page__main-breadcrumb.pf-m-shadow-bottom,\n .pf-c-page__main-section.pf-m-shadow-bottom,\n .pf-c-page__main-wizard.pf-m-shadow-bottom,\n .pf-c-page__main-group.pf-m-shadow-bottom {\n z-index: var(--pf-c-page--section--m-shadow-bottom--ZIndex);\n box-shadow: var(--pf-c-page--section--m-shadow-bottom--BoxShadow); }\n .pf-c-page__main-nav.pf-m-shadow-top,\n .pf-c-page__main-breadcrumb.pf-m-shadow-top,\n .pf-c-page__main-section.pf-m-shadow-top,\n .pf-c-page__main-wizard.pf-m-shadow-top,\n .pf-c-page__main-group.pf-m-shadow-top {\n z-index: var(--pf-c-page--section--m-shadow-top--ZIndex);\n box-shadow: var(--pf-c-page--section--m-shadow-top--BoxShadow); }\n\n.pf-c-page__main,\n.pf-c-page__drawer {\n grid-area: main;\n z-index: var(--pf-c-page__main--ZIndex);\n overflow-x: hidden;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch; }\n .pf-c-page__main:focus,\n .pf-c-page__drawer:focus {\n outline: 0; }\n\n.pf-c-page__main,\n.pf-c-page__main-drawer,\n.pf-c-page__main-group {\n display: flex;\n flex-direction: column; }\n\n.pf-c-page__main-nav {\n padding-top: var(--pf-c-page__main-nav--PaddingTop);\n padding-right: var(--pf-c-page__main-nav--PaddingRight);\n padding-left: var(--pf-c-page__main-nav--PaddingLeft);\n background-color: var(--pf-c-page__main-nav--BackgroundColor); }\n .pf-c-page__main-nav.pf-m-sticky-top,\n .pf-c-page__main-group.pf-m-sticky-top .pf-c-page__main-nav:last-child {\n padding-bottom: var(--pf-c-page__main-nav--m-sticky-top--PaddingBottom); }\n\n.pf-c-page__main-breadcrumb {\n padding: var(--pf-c-page__main-breadcrumb--PaddingTop) var(--pf-c-page__main-breadcrumb--PaddingRight) var(--pf-c-page__main-breadcrumb--PaddingBottom) var(--pf-c-page__main-breadcrumb--PaddingLeft);\n background-color: var(--pf-c-page__main-breadcrumb--BackgroundColor); }\n .pf-c-page__main-breadcrumb + .pf-c-page__main-section {\n --pf-c-page__main-section--PaddingTop: var(--pf-c-page__main-breadcrumb--main-section--PaddingTop); }\n .pf-c-page__main-breadcrumb.pf-m-sticky-top,\n .pf-c-page__main-group.pf-m-sticky-top .pf-c-page__main-breadcrumb:last-child {\n --pf-c-page__main-breadcrumb--PaddingBottom: var(--pf-c-page__main-breadcrumb--m-sticky-top--PaddingBottom); }\n\n.pf-c-page__main-section:last-child, .pf-c-page__main-section:only-child, .pf-c-page__main-section.pf-m-fill,\n.pf-c-page__main-group:last-child,\n.pf-c-page__main-group:only-child,\n.pf-c-page__main-group.pf-m-fill,\n.pf-c-page__main-wizard:last-child,\n.pf-c-page__main-wizard:only-child,\n.pf-c-page__main-wizard.pf-m-fill {\n flex-grow: 1; }\n\n.pf-c-page__main-section.pf-m-no-fill,\n.pf-c-page__main-group.pf-m-no-fill,\n.pf-c-page__main-wizard.pf-m-no-fill {\n flex-grow: 0; }\n\n.pf-c-page__main-section {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft);\n background-color: var(--pf-c-page__main-section--BackgroundColor); }\n .pf-c-page__main-section.pf-m-light {\n --pf-c-page__main-section--BackgroundColor: var(--pf-c-page__main-section--m-light--BackgroundColor); }\n .pf-c-page__main-section[class*=\"pf-m-dark-\"] {\n color: var(--pf-global--Color--100); }\n .pf-c-page__main-section.pf-m-dark-100 {\n --pf-c-page__main-section--BackgroundColor: var(--pf-c-page__main-section--m-dark-100--BackgroundColor); }\n .pf-c-page__main-section.pf-m-dark-200 {\n --pf-c-page__main-section--BackgroundColor: var(--pf-c-page__main-section--m-dark-200--BackgroundColor); }\n .pf-c-page__main-section.pf-m-padding {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n .pf-c-page__main-section.pf-m-no-padding {\n --pf-c-page__main-section--PaddingTop: 0;\n --pf-c-page__main-section--PaddingRight: 0;\n --pf-c-page__main-section--PaddingBottom: 0;\n --pf-c-page__main-section--PaddingLeft: 0; }\n @media (min-width: 576px) {\n .pf-c-page__main-section.pf-m-padding-on-sm {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n .pf-c-page__main-section.pf-m-no-padding-on-sm {\n --pf-c-page__main-section--PaddingTop: 0;\n --pf-c-page__main-section--PaddingRight: 0;\n --pf-c-page__main-section--PaddingBottom: 0;\n --pf-c-page__main-section--PaddingLeft: 0; } }\n @media (min-width: 768px) {\n .pf-c-page__main-section.pf-m-padding-on-md {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n .pf-c-page__main-section.pf-m-no-padding-on-md {\n --pf-c-page__main-section--PaddingTop: 0;\n --pf-c-page__main-section--PaddingRight: 0;\n --pf-c-page__main-section--PaddingBottom: 0;\n --pf-c-page__main-section--PaddingLeft: 0; } }\n @media (min-width: 992px) {\n .pf-c-page__main-section.pf-m-padding-on-lg {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n .pf-c-page__main-section.pf-m-no-padding-on-lg {\n --pf-c-page__main-section--PaddingTop: 0;\n --pf-c-page__main-section--PaddingRight: 0;\n --pf-c-page__main-section--PaddingBottom: 0;\n --pf-c-page__main-section--PaddingLeft: 0; } }\n @media (min-width: 1200px) {\n .pf-c-page__main-section.pf-m-padding-on-xl {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n .pf-c-page__main-section.pf-m-no-padding-on-xl {\n --pf-c-page__main-section--PaddingTop: 0;\n --pf-c-page__main-section--PaddingRight: 0;\n --pf-c-page__main-section--PaddingBottom: 0;\n --pf-c-page__main-section--PaddingLeft: 0; } }\n @media (min-width: 1450px) {\n .pf-c-page__main-section.pf-m-padding-on-2xl {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n .pf-c-page__main-section.pf-m-no-padding-on-2xl {\n --pf-c-page__main-section--PaddingTop: 0;\n --pf-c-page__main-section--PaddingRight: 0;\n --pf-c-page__main-section--PaddingBottom: 0;\n --pf-c-page__main-section--PaddingLeft: 0; } }\n\n.pf-c-page__main-wizard {\n flex-grow: 1;\n background-color: var(--pf-c-page__main-wizard--BackgroundColor);\n border-top: var(--pf-c-page__main-wizard--BorderTopWidth) solid var(--pf-c-page__main-wizard--BorderTopColor); }\n\n.pf-c-page__main-group {\n flex-shrink: 0; }\n\n.pf-c-page__main-nav .pf-c-page__main-body {\n padding-top: var(--pf-c-page__main-nav--PaddingTop);\n padding-right: var(--pf-c-page__main-nav--PaddingRight);\n padding-left: var(--pf-c-page__main-nav--PaddingLeft); }\n\n.pf-c-page__main-breadcrumb .pf-c-page__main-body {\n padding: var(--pf-c-page__main-breadcrumb--PaddingTop) var(--pf-c-page__main-breadcrumb--PaddingRight) var(--pf-c-page__main-breadcrumb--PaddingBottom) var(--pf-c-page__main-breadcrumb--PaddingLeft); }\n\n.pf-c-page__main-section .pf-c-page__main-body {\n padding: var(--pf-c-page__main-section--PaddingTop) var(--pf-c-page__main-section--PaddingRight) var(--pf-c-page__main-section--PaddingBottom) var(--pf-c-page__main-section--PaddingLeft); }\n\n.pf-c-page__drawer {\n grid-area: main; }\n .pf-c-page__drawer > .pf-c-drawer {\n flex: 1 0 auto; }\n\n.pf-c-pagination {\n --pf-c-pagination--child--MarginRight: var(--pf-global--spacer--lg);\n --pf-c-pagination--m-bottom--child--MarginRight: 0;\n --pf-c-pagination--m-bottom--child--md--MarginRight: var(--pf-global--spacer--lg);\n --pf-c-pagination--m-compact--child--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-pagination--c-options-menu__toggle--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-pagination__nav--Display: none;\n --pf-c-pagination__nav--Visibility: hidden;\n --pf-c-pagination--m-display-summary__nav--Display: none;\n --pf-c-pagination--m-display-summary__nav--Visibility: hidden;\n --pf-c-pagination--m-display-full__nav--Display: inline-flex;\n --pf-c-pagination--m-display-full__nav--Visibility: visible;\n --pf-c-pagination__nav-control--c-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-pagination__nav-control--c-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-pagination__nav-control--c-button--FontSize: var(--pf-global--FontSize--md);\n --pf-c-pagination--m-bottom__nav-control--c-button--OutlineOffset: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-pagination--m-compact__nav-control--nav-control--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-pagination__nav-page-select--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-pagination__nav-page-select--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-pagination__nav-page-select--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-pagination__nav-page-select--child--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-pagination__nav-page-select--c-form-control--width-base: 3.5ch;\n --pf-c-pagination__nav-page-select--c-form-control--width-chars: 2;\n --pf-c-pagination__nav-page-select--c-form-control--Width: calc(var(--pf-c-pagination__nav-page-select--c-form-control--width-base) + (var(--pf-c-pagination__nav-page-select--c-form-control--width-chars) * 1ch));\n --pf-c-pagination__total-items--Display: block;\n --pf-c-pagination__total-items--Visibility: visible;\n --pf-c-pagination--m-display-summary__total-items--Display: block;\n --pf-c-pagination--m-display-summary__total-items--Visibility: visible;\n --pf-c-pagination--m-display-full__total-items--Display: none;\n --pf-c-pagination--m-display-full__total-items--Visibility: hidden;\n --pf-c-pagination--m-sticky--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-pagination--m-sticky--BoxShadow: var(--pf-global--BoxShadow--sm-bottom);\n --pf-c-pagination--m-sticky--md--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-pagination--m-sticky--md--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-pagination--m-sticky--md--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-pagination--m-sticky--md--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-pagination--m-sticky--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-pagination--m-sticky--Top: 0;\n --pf-c-pagination--m-bottom--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-pagination--m-bottom--BoxShadow: var(--pf-global--BoxShadow--sm-top);\n --pf-c-pagination--m-bottom--Bottom: 0;\n --pf-c-pagination--m-bottom--md--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom--md--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom--md--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom--md--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-pagination--m-bottom--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-pagination--m-bottom--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-pagination--m-bottom--m-sticky--BoxShadow: var(--pf-global--BoxShadow--sm-top);\n --pf-c-pagination--c-options-menu--Display: none;\n --pf-c-pagination--c-options-menu--Visibility: hidden;\n --pf-c-pagination--m-display-summary--c-options-menu--Display: none;\n --pf-c-pagination--m-display-summary--c-options-menu--Visibility: hidden;\n --pf-c-pagination--m-display-full--c-options-menu--Display: inline-flex;\n --pf-c-pagination--m-display-full--c-options-menu--Visibility: visible;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end; }\n @media screen and (min-width: 768px) {\n .pf-c-pagination {\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingTop: var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingTop);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight: var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingRight);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingBottom: var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingBottom);\n --pf-c-pagination--m-bottom__nav-control--c-button--PaddingLeft: var(--pf-c-pagination--m-bottom__nav-control--c-button--md--PaddingLeft);\n --pf-c-pagination--m-bottom--child--MarginRight: var(--pf-c-pagination--m-bottom--child--md--MarginRight);\n --pf-c-pagination--m-bottom__nav-control--c-button--OutlineOffset: 0;\n --pf-c-pagination--m-bottom--BoxShadow: none;\n --pf-c-pagination--c-options-menu--Display: inline-flex;\n --pf-c-pagination--c-options-menu--Visibility: visible;\n --pf-c-pagination__nav--Display: inline-flex;\n --pf-c-pagination__nav--Visibility: visible;\n --pf-c-pagination__total-items--Display: none;\n --pf-c-pagination__total-items--Visibility: hidden; } }\n @media screen and (min-width: 1200px) {\n .pf-c-pagination {\n --pf-c-pagination--m-bottom--md--PaddingRight: var(--pf-c-pagination--m-bottom--xl--PaddingRight);\n --pf-c-pagination--m-bottom--md--PaddingLeft: var(--pf-c-pagination--m-bottom--xl--PaddingLeft); } }\n .pf-c-pagination > *:not(:last-child):not(.pf-c-pagination__total-items) {\n margin-right: var(--pf-c-pagination--child--MarginRight); }\n .pf-c-pagination .pf-c-options-menu {\n display: var(--pf-c-pagination--c-options-menu--Display);\n visibility: var(--pf-c-pagination--c-options-menu--Visibility); }\n .pf-c-pagination.pf-m-bottom {\n --pf-c-pagination--child--MarginRight: var(--pf-c-pagination--m-bottom--child--MarginRight);\n --pf-c-pagination__nav-control--c-button--PaddingRight: var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight);\n --pf-c-pagination__nav-control--c-button--PaddingLeft: var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingRight);\n --pf-c-pagination--m-sticky--BoxShadow: var(--pf-c-pagination--m-bottom--m-sticky--BoxShadow);\n --pf-c-pagination--m-sticky--Top: auto;\n position: sticky;\n bottom: var(--pf-c-pagination--m-bottom--Bottom);\n justify-content: center;\n background-color: var(--pf-c-pagination--m-bottom--BackgroundColor);\n box-shadow: var(--pf-c-pagination--m-bottom--BoxShadow); }\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control .pf-c-button {\n --pf-c-button--PaddingTop: var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingTop);\n --pf-c-button--PaddingBottom: var(--pf-c-pagination--m-bottom__nav-control--c-button--PaddingBottom);\n outline-offset: var(--pf-c-pagination--m-bottom__nav-control--c-button--OutlineOffset); }\n .pf-c-pagination.pf-m-bottom.pf-m-static {\n --pf-c-pagination--m-bottom--MarginTop: 0;\n --pf-c-pagination--m-bottom--BorderTopWidth: 0;\n position: relative;\n box-shadow: none; }\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-first,\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-last,\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-page-select {\n display: none;\n visibility: hidden; }\n .pf-c-pagination.pf-m-bottom .pf-c-options-menu {\n position: absolute;\n display: block;\n visibility: visible; }\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav {\n display: flex;\n flex-basis: 100%;\n justify-content: space-between;\n visibility: visible; }\n @media screen and (min-width: 768px) {\n .pf-c-pagination.pf-m-bottom {\n --pf-c-pagination--m-bottom--BorderTopWidth: 0;\n --pf-c-pagination--m-bottom--MarginTop: 0;\n --pf-c-pagination--m-bottom--Bottom: auto;\n position: relative;\n justify-content: flex-end;\n padding: var(--pf-c-pagination--m-bottom--md--PaddingTop) var(--pf-c-pagination--m-bottom--md--PaddingRight) var(--pf-c-pagination--m-bottom--md--PaddingBottom) var(--pf-c-pagination--m-bottom--md--PaddingLeft); }\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-first,\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-control.pf-m-last,\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav-page-select {\n display: block;\n visibility: visible; }\n .pf-c-pagination.pf-m-bottom .pf-c-options-menu {\n position: relative; }\n .pf-c-pagination.pf-m-bottom .pf-c-pagination__nav {\n display: inline-flex;\n flex-basis: auto; } }\n .pf-c-pagination.pf-m-sticky {\n --pf-c-pagination--m-bottom--Bottom: 0;\n position: sticky;\n top: var(--pf-c-pagination--m-sticky--Top);\n z-index: var(--pf-c-pagination--m-sticky--ZIndex);\n padding-top: var(--pf-c-pagination--m-sticky--PaddingTop);\n padding-right: var(--pf-c-pagination--m-sticky--PaddingRight);\n padding-bottom: var(--pf-c-pagination--m-sticky--PaddingBottom);\n padding-left: var(--pf-c-pagination--m-sticky--PaddingLeft);\n background-color: var(--pf-c-pagination--m-sticky--BackgroundColor);\n box-shadow: var(--pf-c-pagination--m-sticky--BoxShadow); }\n @media screen and (min-width: 768px) {\n .pf-c-pagination.pf-m-sticky {\n padding: var(--pf-c-pagination--m-sticky--md--PaddingTop) var(--pf-c-pagination--m-sticky--md--PaddingRight) var(--pf-c-pagination--m-sticky--md--PaddingBottom) var(--pf-c-pagination--m-sticky--md--PaddingLeft); } }\n .pf-c-pagination .pf-c-options-menu__toggle {\n font-size: var(--pf-c-pagination--c-options-menu__toggle--FontSize); }\n .pf-c-pagination.pf-m-compact {\n --pf-c-pagination--child--MarginRight: var(--pf-c-pagination--m-compact--child--MarginRight); }\n\n.pf-c-pagination__nav {\n display: var(--pf-c-pagination__nav--Display);\n justify-content: flex-end;\n visibility: var(--pf-c-pagination__nav--Visibility); }\n\n.pf-c-pagination__nav-control .pf-c-button {\n padding-right: var(--pf-c-pagination__nav-control--c-button--PaddingRight);\n padding-left: var(--pf-c-pagination__nav-control--c-button--PaddingLeft);\n font-size: var(--pf-c-pagination__nav-control--c-button--FontSize); }\n\n.pf-c-pagination.pf-m-compact .pf-c-pagination__nav-control + .pf-c-pagination__nav-control {\n margin-left: var(--pf-c-pagination--m-compact__nav-control--nav-control--MarginLeft); }\n\n.pf-c-pagination__nav-page-select {\n display: flex;\n align-items: center;\n padding-right: var(--pf-c-pagination__nav-page-select--PaddingRight);\n padding-left: var(--pf-c-pagination__nav-page-select--PaddingLeft); }\n .pf-c-pagination__nav-page-select > * {\n font-size: var(--pf-c-pagination__nav-page-select--FontSize);\n white-space: nowrap; }\n .pf-c-pagination__nav-page-select > *:not(:last-child) {\n margin-right: var(--pf-c-pagination__nav-page-select--child--MarginRight); }\n .pf-c-pagination__nav-page-select .pf-c-form-control {\n width: var(--pf-c-pagination__nav-page-select--c-form-control--Width); }\n\n.pf-c-pagination__total-items {\n display: var(--pf-c-pagination__total-items--Display);\n visibility: var(--pf-c-pagination__total-items--Visibility); }\n\n.pf-c-pagination.pf-m-display-summary {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-summary__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-summary__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-summary--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-summary__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-summary__total-items--Visibility); }\n\n.pf-c-pagination.pf-m-display-full {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-full__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-full__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-full--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-full__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-full__total-items--Visibility); }\n\n@media (min-width: 576px) {\n .pf-c-pagination.pf-m-display-summary-on-sm {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-summary__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-summary__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-summary--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-summary__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-summary__total-items--Visibility); }\n .pf-c-pagination.pf-m-display-full-on-sm {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-full__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-full__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-full--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-full__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-full__total-items--Visibility); } }\n\n@media (min-width: 768px) {\n .pf-c-pagination.pf-m-display-summary-on-md {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-summary__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-summary__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-summary--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-summary__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-summary__total-items--Visibility); }\n .pf-c-pagination.pf-m-display-full-on-md {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-full__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-full__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-full--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-full__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-full__total-items--Visibility); } }\n\n@media (min-width: 992px) {\n .pf-c-pagination.pf-m-display-summary-on-lg {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-summary__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-summary__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-summary--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-summary__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-summary__total-items--Visibility); }\n .pf-c-pagination.pf-m-display-full-on-lg {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-full__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-full__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-full--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-full__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-full__total-items--Visibility); } }\n\n@media (min-width: 1200px) {\n .pf-c-pagination.pf-m-display-summary-on-xl {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-summary__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-summary__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-summary--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-summary__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-summary__total-items--Visibility); }\n .pf-c-pagination.pf-m-display-full-on-xl {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-full__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-full__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-full--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-full__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-full__total-items--Visibility); } }\n\n@media (min-width: 1450px) {\n .pf-c-pagination.pf-m-display-summary-on-2xl {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-summary__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-summary__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-summary--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-summary--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-summary__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-summary__total-items--Visibility); }\n .pf-c-pagination.pf-m-display-full-on-2xl {\n --pf-c-pagination__nav--Display: var(--pf-c-pagination--m-display-full__nav--Display);\n --pf-c-pagination__nav--Visibility: var(--pf-c-pagination--m-display-full__nav--Visibility);\n --pf-c-pagination--c-options-menu--Display: var(--pf-c-pagination--m-display-full--c-options-menu--Display);\n --pf-c-pagination--c-options-menu--Visibility: var(--pf-c-pagination--m-display-full--c-options-menu--Visibility);\n --pf-c-pagination__total-items--Display: var(--pf-c-pagination--m-display-full__total-items--Display);\n --pf-c-pagination__total-items--Visibility: var(--pf-c-pagination--m-display-full__total-items--Visibility); } }\n\n.pf-c-popover {\n --pf-c-popover--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-popover--MinWidth: calc(var(--pf-c-popover__content--PaddingLeft) + var(--pf-c-popover__content--PaddingRight) + 18.75rem);\n --pf-c-popover--MaxWidth: calc(var(--pf-c-popover__content--PaddingLeft) + var(--pf-c-popover__content--PaddingRight) + 18.75rem);\n --pf-c-popover--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-popover__content--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-popover__content--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-popover__content--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-popover__content--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-popover__content--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-popover__arrow--Width: var(--pf-global--arrow--width-lg);\n --pf-c-popover__arrow--Height: var(--pf-global--arrow--width-lg);\n --pf-c-popover__arrow--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-popover__arrow--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-popover__arrow--m-top--TranslateX: -50%;\n --pf-c-popover__arrow--m-top--TranslateY: 50%;\n --pf-c-popover__arrow--m-top--Rotate: 45deg;\n --pf-c-popover__arrow--m-right--TranslateX: -50%;\n --pf-c-popover__arrow--m-right--TranslateY: -50%;\n --pf-c-popover__arrow--m-right--Rotate: 45deg;\n --pf-c-popover__arrow--m-bottom--TranslateX: -50%;\n --pf-c-popover__arrow--m-bottom--TranslateY: -50%;\n --pf-c-popover__arrow--m-bottom--Rotate: 45deg;\n --pf-c-popover__arrow--m-left--TranslateX: 50%;\n --pf-c-popover__arrow--m-left--TranslateY: -50%;\n --pf-c-popover__arrow--m-left--Rotate: 45deg;\n --pf-c-popover--c-button--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-popover--c-button--Top: calc(var(--pf-c-popover__content--PaddingTop) - var(--pf-global--spacer--form-element));\n --pf-c-popover--c-button--Right: var(--pf-global--spacer--md);\n --pf-c-popover--c-button--sibling--PaddingRight: var(--pf-global--spacer--2xl);\n --pf-c-popover--c-title--MarginBottom: var(--pf-global--spacer--sm);\n --pf-c-popover__footer--MarginTop: var(--pf-global--spacer--md);\n position: relative;\n min-width: var(--pf-c-popover--MinWidth);\n max-width: var(--pf-c-popover--MaxWidth);\n font-size: var(--pf-c-popover--FontSize);\n box-shadow: var(--pf-c-popover--BoxShadow); }\n .pf-c-popover.pf-m-no-padding {\n --pf-c-popover__content--PaddingTop: 0px;\n --pf-c-popover__content--PaddingRight: 0px;\n --pf-c-popover__content--PaddingBottom: 0px;\n --pf-c-popover__content--PaddingLeft: 0px; }\n .pf-c-popover.pf-m-width-auto {\n --pf-c-popover--MinWidth: auto;\n --pf-c-popover--MaxWidth: none; }\n .pf-c-popover.pf-m-top .pf-c-popover__arrow {\n bottom: 0;\n left: 50%;\n transform: translateX(var(--pf-c-popover__arrow--m-top--TranslateX)) translateY(var(--pf-c-popover__arrow--m-top--TranslateY)) rotate(var(--pf-c-popover__arrow--m-top--Rotate)); }\n .pf-c-popover.pf-m-bottom .pf-c-popover__arrow {\n top: 0;\n left: 50%;\n transform: translateX(var(--pf-c-popover__arrow--m-bottom--TranslateX)) translateY(var(--pf-c-popover__arrow--m-bottom--TranslateY)) rotate(var(--pf-c-popover__arrow--m-bottom--Rotate)); }\n .pf-c-popover.pf-m-left .pf-c-popover__arrow {\n top: 50%;\n right: 0;\n transform: translateX(var(--pf-c-popover__arrow--m-left--TranslateX)) translateY(var(--pf-c-popover__arrow--m-left--TranslateY)) rotate(var(--pf-c-popover__arrow--m-left--Rotate)); }\n .pf-c-popover.pf-m-right .pf-c-popover__arrow {\n top: 50%;\n left: 0;\n transform: translateX(var(--pf-c-popover__arrow--m-right--TranslateX)) translateY(var(--pf-c-popover__arrow--m-right--TranslateY)) rotate(var(--pf-c-popover__arrow--m-right--Rotate)); }\n\n.pf-c-popover__content {\n position: relative;\n padding: var(--pf-c-popover__content--PaddingTop) var(--pf-c-popover__content--PaddingRight) var(--pf-c-popover__content--PaddingBottom) var(--pf-c-popover__content--PaddingLeft);\n background-color: var(--pf-c-popover__content--BackgroundColor); }\n .pf-c-popover__content > .pf-c-title {\n margin-bottom: var(--pf-c-popover--c-title--MarginBottom); }\n .pf-c-popover__content > .pf-c-button {\n position: absolute;\n top: var(--pf-c-popover--c-button--Top);\n right: var(--pf-c-popover--c-button--Right); }\n .pf-c-popover__content > .pf-c-button + * {\n padding-right: var(--pf-c-popover--c-button--sibling--PaddingRight); }\n\n.pf-c-popover__arrow {\n position: absolute;\n width: var(--pf-c-popover__arrow--Width);\n height: var(--pf-c-popover__arrow--Height);\n pointer-events: none;\n background-color: var(--pf-c-popover__arrow--BackgroundColor);\n box-shadow: var(--pf-c-popover__arrow--BoxShadow); }\n\n.pf-c-popover__body {\n word-wrap: break-word; }\n\n.pf-c-popover__footer {\n margin-top: var(--pf-c-popover__footer--MarginTop); }\n\n.pf-c-progress {\n --pf-c-progress--GridGap: var(--pf-global--spacer--md);\n --pf-c-progress__bar--before--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-progress__bar--Height: var(--pf-global--spacer--md);\n --pf-c-progress__bar--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-progress__measure--m-static-width--MinWidth: 4.5ch;\n --pf-c-progress__status-icon--Color: var(--pf-global--Color--100);\n --pf-c-progress__status-icon--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-progress__bar--before--Opacity: .2;\n --pf-c-progress__indicator--Height: var(--pf-c-progress__bar--Height);\n --pf-c-progress__indicator--BackgroundColor: var(--pf-c-progress__bar--before--BackgroundColor);\n --pf-c-progress--m-success__bar--BackgroundColor: var(--pf-global--success-color--100);\n --pf-c-progress--m-warning__bar--BackgroundColor: var(--pf-global--warning-color--100);\n --pf-c-progress--m-danger__bar--BackgroundColor: var(--pf-global--danger-color--100);\n --pf-c-progress--m-success__status-icon--Color: var(--pf-global--success-color--100);\n --pf-c-progress--m-warning__status-icon--Color: var(--pf-global--warning-color--100);\n --pf-c-progress--m-danger__status-icon--Color: var(--pf-global--danger-color--100);\n --pf-c-progress--m-inside__indicator--MinWidth: var(--pf-global--spacer--xl);\n --pf-c-progress--m-inside__measure--Color: var(--pf-global--Color--light-100);\n --pf-c-progress--m-success--m-inside__measure--Color: var(--pf-global--Color--light-100);\n --pf-c-progress--m-warning--m-inside__measure--Color: var(--pf-global--Color--dark-100);\n --pf-c-progress--m-inside__measure--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-progress--m-outside__measure--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-progress--m-sm__bar--Height: var(--pf-global--spacer--sm);\n --pf-c-progress--m-sm__description--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-progress--m-sm__measure--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-progress--m-lg__bar--Height: var(--pf-global--spacer--lg);\n display: grid;\n align-items: end;\n grid-gap: var(--pf-c-progress--GridGap);\n grid-template-columns: auto auto;\n grid-template-rows: 1fr auto; }\n .pf-c-progress.pf-m-sm {\n --pf-c-progress__bar--Height: var(--pf-c-progress--m-sm__bar--Height); }\n .pf-c-progress.pf-m-sm .pf-c-progress__description {\n font-size: var(--pf-c-progress--m-sm__description--FontSize); }\n .pf-c-progress.pf-m-sm .pf-c-progress__measure {\n font-size: var(--pf-c-progress--m-sm__measure--FontSize); }\n .pf-c-progress.pf-m-lg {\n --pf-c-progress__bar--Height: var(--pf-c-progress--m-lg__bar--Height); }\n .pf-c-progress.pf-m-inside .pf-c-progress__indicator {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pf-c-progress--m-inside__indicator--MinWidth); }\n .pf-c-progress.pf-m-inside .pf-c-progress__measure {\n font-size: var(--pf-c-progress--m-inside__measure--FontSize);\n color: var(--pf-c-progress--m-inside__measure--Color);\n text-align: center; }\n .pf-c-progress.pf-m-outside .pf-c-progress__description {\n grid-column: 1 / 3; }\n .pf-c-progress.pf-m-outside .pf-c-progress__status {\n grid-column: 2 / 3;\n grid-row: 2 / 3;\n align-self: center; }\n .pf-c-progress.pf-m-outside .pf-c-progress__measure {\n display: inline-block;\n font-size: var(--pf-c-progress--m-outside__measure--FontSize); }\n .pf-c-progress.pf-m-outside .pf-c-progress__measure.pf-m-static-width {\n min-width: var(--pf-c-progress__measure--m-static-width--MinWidth);\n text-align: left; }\n .pf-c-progress.pf-m-outside .pf-c-progress__bar,\n .pf-c-progress.pf-m-outside .pf-c-progress__indicator {\n grid-column: 1 / 2; }\n .pf-c-progress.pf-m-singleline {\n grid-template-rows: 1fr; }\n .pf-c-progress.pf-m-singleline .pf-c-progress__description {\n display: none;\n visibility: hidden; }\n .pf-c-progress.pf-m-singleline .pf-c-progress__bar {\n grid-row: 1 / 2;\n grid-column: 1 / 2; }\n .pf-c-progress.pf-m-singleline .pf-c-progress__status {\n grid-row: 1 / 2;\n grid-column: 2 / 3; }\n .pf-c-progress.pf-m-outside, .pf-c-progress.pf-m-singleline {\n grid-template-columns: 1fr fit-content(50%); }\n .pf-c-progress.pf-m-success {\n --pf-c-progress__bar--before--BackgroundColor: var(--pf-c-progress--m-success__bar--BackgroundColor);\n --pf-c-progress__status-icon--Color: var(--pf-c-progress--m-success__status-icon--Color);\n --pf-c-progress--m-inside__measure--Color: var(--pf-c-progress--m-success--m-inside__measure--Color); }\n .pf-c-progress.pf-m-warning {\n --pf-c-progress__bar--before--BackgroundColor: var(--pf-c-progress--m-warning__bar--BackgroundColor);\n --pf-c-progress__status-icon--Color: var(--pf-c-progress--m-warning__status-icon--Color);\n --pf-c-progress--m-inside__measure--Color: var(--pf-c-progress--m-warning--m-inside__measure--Color); }\n .pf-c-progress.pf-m-danger {\n --pf-c-progress__bar--before--BackgroundColor: var(--pf-c-progress--m-danger__bar--BackgroundColor);\n --pf-c-progress__status-icon--Color: var(--pf-c-progress--m-danger__status-icon--Color); }\n\n.pf-c-progress__description {\n word-break: break-word;\n grid-column: 1 / 2; }\n .pf-c-progress__description.pf-m-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n\n.pf-c-progress__status {\n grid-column: 2 / 3;\n grid-row: 1 / 2;\n text-align: right;\n word-break: break-word; }\n\n.pf-c-progress__status-icon {\n margin-left: var(--pf-c-progress__status-icon--MarginLeft);\n color: var(--pf-c-progress__status-icon--Color); }\n\n.pf-c-progress__bar {\n position: relative;\n grid-column: 1 / 3;\n grid-row: 2 / 3;\n align-self: center;\n height: var(--pf-c-progress__bar--Height);\n background-color: var(--pf-c-progress__bar--BackgroundColor); }\n .pf-c-progress__bar::before {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n content: \"\";\n background-color: var(--pf-c-progress__bar--before--BackgroundColor);\n opacity: var(--pf-c-progress__bar--before--Opacity); }\n\n.pf-c-progress__indicator {\n position: absolute;\n top: 0;\n left: 0;\n height: var(--pf-c-progress__indicator--Height);\n background-color: var(--pf-c-progress__indicator--BackgroundColor); }\n\n.pf-c-radio {\n --pf-c-radio--GridGap: var(--pf-global--spacer--xs) var(--pf-global--spacer--sm);\n --pf-c-radio__label--disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-radio__label--Color: var(--pf-global--Color--100);\n --pf-c-radio__label--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-radio__label--FontSize: var(--pf-global--FontSize--md);\n --pf-c-radio__label--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-radio__input--MarginTop: -0.1875rem;\n --pf-c-radio__input--first-child--MarginLeft: 0.0625rem;\n --pf-c-radio__input--last-child--MarginRight: 0.0625rem;\n --pf-c-radio__description--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-radio__description--Color: var(--pf-global--Color--200);\n display: grid;\n grid-template-columns: auto 1fr;\n grid-gap: var(--pf-c-radio--GridGap);\n align-items: center;\n justify-items: start; }\n\n.pf-c-radio__label {\n font-size: var(--pf-c-radio__label--FontSize);\n font-weight: var(--pf-c-radio__label--FontWeight);\n line-height: var(--pf-c-radio__label--LineHeight);\n color: var(--pf-c-radio__label--Color); }\n\n.pf-c-radio__input {\n margin-top: var(--pf-c-radio__input--MarginTop); }\n .pf-c-radio__input:first-child {\n margin-left: var(--pf-c-radio__input--first-child--MarginLeft); }\n .pf-c-radio__input:last-child {\n margin-right: var(--pf-c-radio__input--last-child--MarginRight); }\n\n.pf-c-radio__description {\n grid-column: 2;\n font-size: var(--pf-c-radio__description--FontSize);\n color: var(--pf-c-radio__description--Color); }\n\nlabel.pf-c-radio, .pf-c-radio__label,\n.pf-c-radio__input {\n cursor: pointer; }\n\n.pf-c-radio__label:disabled, .pf-c-radio__label.pf-m-disabled,\n.pf-c-radio__input:disabled,\n.pf-c-radio__input.pf-m-disabled {\n --pf-c-radio__label--Color: var(--pf-c-radio__label--disabled--Color);\n cursor: not-allowed; }\n\n.pf-c-search-input {\n --pf-c-search-input__text--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-search-input__text--before--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-search-input__text--after--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-search-input__text--after--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-search-input--hover__text--after--BorderBottomColor: var(--pf-global--primary-color--100);\n --pf-c-search-input__text--focus-within--after--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-search-input__text--focus-within--after--BorderBottomColor: var(--pf-global--primary-color--100);\n --pf-c-search-input__text-input--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-search-input__text-input--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-search-input__text-input--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-search-input__text-input--PaddingLeft: var(--pf-global--spacer--xl);\n --pf-c-search-input__text-input--MinWidth: 6ch;\n --pf-c-search-input__icon--Left: var(--pf-global--spacer--sm);\n --pf-c-search-input__icon--Color: var(--pf-global--Color--200);\n --pf-c-search-input__text--hover__icon--Color: var(--pf-global--Color--100);\n --pf-c-search-input__icon--TranslateY: -50%;\n --pf-c-search-input__utilities--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-search-input__utilities--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-search-input__utilities--child--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-search-input__utilities--c-button--PaddingRight: var(--pf-global--spacer--xs);\n --pf-c-search-input__utilities--c-button--PaddingLeft: var(--pf-global--spacer--xs);\n position: relative;\n display: flex;\n padding: var(--pf-c-search-input--PaddingTop) var(--pf-c-search-input--PaddingRight) var(--pf-c-search-input--PaddingBottom) var(--pf-c-search-input--PaddingLeft); }\n .pf-c-search-input:hover {\n --pf-c-search-input__text--after--BorderBottomColor: var(--pf-c-search-input--hover__text--after--BorderBottomColor); }\n\n.pf-c-search-input__text {\n flex: 1; }\n .pf-c-search-input__text::before, .pf-c-search-input__text::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n content: \"\"; }\n .pf-c-search-input__text::before {\n border: var(--pf-c-search-input__text--before--BorderWidth) solid var(--pf-c-search-input__text--before--BorderColor); }\n .pf-c-search-input__text::after {\n border-bottom: var(--pf-c-search-input__text--after--BorderBottomWidth) solid var(--pf-c-search-input__text--after--BorderBottomColor); }\n .pf-c-search-input__text:hover, .pf-c-search-input__text:focus-within {\n --pf-c-search-input__icon--Color: var(--pf-c-search-input__text--hover__icon--Color); }\n .pf-c-search-input__text:focus-within {\n --pf-c-search-input__text--after--BorderBottomWidth: var(--pf-c-search-input__text--focus-within--after--BorderBottomWidth);\n --pf-c-search-input__text--after--BorderBottomColor: var(--pf-c-search-input__text--focus-within--after--BorderBottomColor); }\n\n.pf-c-search-input__icon {\n position: absolute;\n top: 50%;\n left: var(--pf-c-search-input__icon--Left);\n color: var(--pf-c-search-input__icon--Color);\n transform: translateY(var(--pf-c-search-input__icon--TranslateY)); }\n\n.pf-c-search-input__text-input {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n position: relative;\n width: 100%;\n min-width: var(--pf-c-search-input__text-input--MinWidth);\n padding: var(--pf-c-search-input__text-input--PaddingTop) var(--pf-c-search-input__text-input--PaddingRight) var(--pf-c-search-input__text-input--PaddingBottom) var(--pf-c-search-input__text-input--PaddingLeft);\n border: 0; }\n\n.pf-c-search-input__utilities {\n display: flex;\n margin-right: var(--pf-c-search-input__utilities--MarginRight);\n margin-left: var(--pf-c-search-input__utilities--MarginLeft); }\n .pf-c-search-input__utilities > * + * {\n margin-left: var(--pf-c-search-input__utilities--child--MarginLeft); }\n .pf-c-search-input__utilities .pf-c-button {\n --pf-c-button--PaddingRight: var(--pf-c-search-input__utilities--c-button--PaddingRight);\n --pf-c-button--PaddingLeft: var(--pf-c-search-input__utilities--c-button--PaddingLeft); }\n\n.pf-c-search-input__nav {\n display: flex; }\n\n.pf-c-search-input__count {\n display: flex;\n align-items: center; }\n\n.pf-c-select {\n color: var(--pf-global--Color--100);\n --pf-c-select__toggle--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-select__toggle--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-select__toggle--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-select__toggle--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-select__toggle--MinWidth: var(--pf-global--target-size--MinWidth);\n --pf-c-select__toggle--FontSize: var(--pf-global--FontSize--md);\n --pf-c-select__toggle--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-select__toggle--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-select__toggle--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-select__toggle--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-select__toggle--before--BorderTopColor: var(--pf-global--BorderColor--300);\n --pf-c-select__toggle--before--BorderRightColor: var(--pf-global--BorderColor--300);\n --pf-c-select__toggle--before--BorderBottomColor: var(--pf-global--BorderColor--200);\n --pf-c-select__toggle--before--BorderLeftColor: var(--pf-global--BorderColor--300);\n --pf-c-select__toggle--Color: var(--pf-global--Color--100);\n --pf-c-select__toggle--hover--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-select__toggle--focus--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-select__toggle--active--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-select__toggle--m-expanded--before--BorderBottomColor: var(--pf-global--active-color--100);\n --pf-c-select__toggle--focus--before--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-select__toggle--active--before--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-select__toggle--m-expanded--before--BorderBottomWidth: var(--pf-global--BorderWidth--md);\n --pf-c-select__toggle--disabled--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-select__toggle--m-plain--before--BorderColor: transparent;\n --pf-c-select__toggle-wrapper--not-last-child--MarginRight: var(--pf-global--spacer--xs);\n --pf-c-select__toggle-wrapper--MaxWidth: calc(100% - var(--pf-global--spacer--lg));\n --pf-c-select__toggle-wrapper--c-chip-group--MarginTop: 0.3125rem;\n --pf-c-select__toggle-wrapper--c-chip-group--MarginBottom: 0.3125rem;\n --pf-c-select__toggle-typeahead--FlexBasis: 10em;\n --pf-c-select__toggle-typeahead--BackgroundColor: transparent;\n --pf-c-select__toggle-typeahead--BorderTop: none;\n --pf-c-select__toggle-typeahead--BorderRight: none;\n --pf-c-select__toggle-typeahead--BorderLeft: none;\n --pf-c-select__toggle-typeahead--MinWidth: 7.5rem;\n --pf-c-select__toggle-typeahead--focus--PaddingBottom: calc(var(--pf-global--spacer--form-element) - var(--pf-global--BorderWidth--sm));\n --pf-c-select__toggle-icon--toggle-text--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-select__toggle-badge--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-select__toggle-arrow--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-select__toggle-arrow--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-select__toggle-arrow--with-clear--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-select__toggle-arrow--m-top--m-expanded__toggle-arrow--Rotate: 180deg;\n --pf-c-select__toggle-clear--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-select__toggle-clear--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-select__toggle-clear--toggle-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-select__toggle-button--Color: var(--pf-global--Color--100);\n --pf-c-select__menu--BackgroundColor: var(--pf-global--BackgroundColor--light-100);\n --pf-c-select__menu--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-select__menu--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-select__menu--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-select__menu--Top: calc(100% + var(--pf-global--spacer--xs));\n --pf-c-select__menu--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-select__menu--m-top--TranslateY: calc(-100% - var(--pf-global--spacer--xs));\n --pf-c-select__menu-item--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-select__menu-item--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-select__menu-item--m-selected--PaddingRight: var(--pf-global--spacer--2xl);\n --pf-c-select__menu-item--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-select__menu-item--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-select__menu-item--FontSize: var(--pf-global--FontSize--md);\n --pf-c-select__menu-item--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-select__menu-item--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-select__menu-item--Color: var(--pf-global--Color--dark-100);\n --pf-c-select__menu-item--Width: 100%;\n --pf-c-select__menu-item--disabled--Color: var(--pf-global--Color--dark-200);\n --pf-c-select__menu-item--hover--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-select__menu-item--focus--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-select__menu-item--disabled--BackgroundColor: transparent;\n --pf-c-select__menu-item--m-link--Width: auto;\n --pf-c-select__menu-item--m-link--hover--BackgroundColor: transparent;\n --pf-c-select__menu-item--m-link--focus--BackgroundColor: transparent;\n --pf-c-select__menu-item--m-action--Color: var(--pf-global--disabled-color--200);\n --pf-c-select__menu-item--m-action--Width: auto;\n --pf-c-select__menu-item--m-action--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-select__menu-item--m-action--hover--BackgroundColor: transparent;\n --pf-c-select__menu-item--m-action--focus--BackgroundColor: transparent;\n --pf-c-select__menu-item--hover__menu-item--m-action--Color: var(--pf-global--Color--200);\n --pf-c-select__menu-item--m-action--hover--Color: var(--pf-global--Color--100);\n --pf-c-select__menu-item--m-action--focus--Color: var(--pf-global--Color--100);\n --pf-c-select__menu-wrapper--m-favorite__menu-item--m-favorite-action--Color: var(--pf-global--palette--gold-400);\n --pf-c-select__menu-item-icon--Color: var(--pf-global--active-color--100);\n --pf-c-select__menu-item-icon--FontSize: var(--pf-global--icon--FontSize--sm);\n --pf-c-select__menu-item-icon--Right: var(--pf-global--spacer--md);\n --pf-c-select__menu-item-icon--Top: 50%;\n --pf-c-select__menu-item-icon--TranslateY: -50%;\n --pf-c-select__menu-item-action-icon--MinHeight: calc(var(--pf-c-select__menu-item--FontSize) * var(--pf-c-select__menu-item--LineHeight));\n --pf-c-select__menu-item--match--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-select__menu-search--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-select__menu-search--PaddingRight: var(--pf-c-select__menu-item--PaddingRight);\n --pf-c-select__menu-search--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-select__menu-search--PaddingLeft: var(--pf-c-select__menu-item--PaddingLeft);\n --pf-c-select__menu-group--menu-group--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-select__menu-group-title--PaddingTop: var(--pf-c-select__menu-item--PaddingTop);\n --pf-c-select__menu-group-title--PaddingRight: var(--pf-c-select__menu-item--PaddingRight);\n --pf-c-select__menu-group-title--PaddingBottom: var(--pf-c-select__menu-item--PaddingBottom);\n --pf-c-select__menu-group-title--PaddingLeft: var(--pf-c-select__menu-item--PaddingLeft);\n --pf-c-select__menu-group-title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-select__menu-group-title--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-select__menu-group-title--Color: var(--pf-global--Color--dark-200);\n --pf-c-select__menu-item-description--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-select__menu-item-description--Color: var(--pf-global--Color--200);\n --pf-c-select__menu-item-description--PaddingRight: var(--pf-c-select__menu-item--PaddingRight);\n --pf-c-select__menu-item-main--PaddingRight: var(--pf-c-select__menu-item--PaddingRight);\n --pf-c-select__menu-item--m-selected__menu-item-main--PaddingRight: var(--pf-c-select__menu-item--m-selected--PaddingRight);\n --pf-c-select-menu--c-divider--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-select-menu--c-divider--MarginBottom: var(--pf-global--spacer--sm);\n position: relative;\n display: inline-block;\n width: 100%; }\n .pf-c-select .pf-c-divider {\n margin-top: var(--pf-c-select-menu--c-divider--MarginTop);\n margin-bottom: var(--pf-c-select-menu--c-divider--MarginBottom); }\n .pf-c-select .pf-c-divider:last-child {\n --pf-c-select-menu--c-divider--MarginBottom: 0; }\n\n.pf-c-select__menu-search + .pf-c-divider {\n --pf-c-select-menu--c-divider--MarginTop: 0; }\n\n.pf-c-select__toggle {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n min-width: var(--pf-c-select__toggle--MinWidth);\n padding: var(--pf-c-select__toggle--PaddingTop) var(--pf-c-select__toggle--PaddingRight) var(--pf-c-select__toggle--PaddingBottom) var(--pf-c-select__toggle--PaddingLeft);\n font-size: var(--pf-c-select__toggle--FontSize);\n font-weight: var(--pf-c-select__toggle--FontWeight);\n line-height: var(--pf-c-select__toggle--LineHeight);\n color: var(--pf-c-select__toggle--Color);\n white-space: nowrap;\n cursor: pointer;\n background-color: var(--pf-c-select__toggle--BackgroundColor);\n border: none; }\n .pf-c-select__toggle.pf-m-disabled, .pf-c-select__toggle:disabled {\n --pf-c-select__toggle--BackgroundColor: var(--pf-c-select__toggle--disabled--BackgroundColor);\n pointer-events: none; }\n .pf-c-select__toggle.pf-m-disabled::before, .pf-c-select__toggle:disabled::before {\n border: 0; }\n .pf-c-select__toggle::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: var(--pf-c-select__toggle--before--BorderWidth) solid;\n border-color: var(--pf-c-select__toggle--before--BorderTopColor) var(--pf-c-select__toggle--before--BorderRightColor) var(--pf-c-select__toggle--before--BorderBottomColor) var(--pf-c-select__toggle--before--BorderLeftColor); }\n .pf-c-select__toggle:hover::before {\n --pf-c-select__toggle--before--BorderBottomColor: var(--pf-c-select__toggle--hover--before--BorderBottomColor); }\n .pf-c-select__toggle:focus::before, .pf-c-select__toggle:focus-within::before {\n --pf-c-select__toggle--before--BorderBottomColor: var(--pf-c-select__toggle--focus--before--BorderBottomColor);\n border-bottom-width: var(--pf-c-select__toggle--focus--before--BorderBottomWidth); }\n .pf-c-select__toggle:active::before, .pf-c-select__toggle.pf-m-active::before {\n --pf-c-select__toggle--before--BorderBottomColor: var(--pf-c-select__toggle--active--before--BorderBottomColor);\n border-bottom-width: var(--pf-c-select__toggle--active--before--BorderBottomWidth); }\n .pf-m-expanded > .pf-c-select__toggle::before {\n --pf-c-select__toggle--before--BorderBottomColor: var(--pf-c-select__toggle--m-expanded--before--BorderBottomColor);\n border-bottom-width: var(--pf-c-select__toggle--m-expanded--before--BorderBottomWidth); }\n .pf-c-select__toggle.pf-m-plain::before {\n border-color: var(--pf-c-select__toggle--m-plain--before--BorderColor); }\n .pf-c-select__toggle.pf-m-typeahead {\n --pf-c-select__toggle--PaddingTop: 0;\n --pf-c-select__toggle--PaddingRight: 0;\n --pf-c-select__toggle--PaddingBottom: 0; }\n .pf-c-select__toggle.pf-m-typeahead .pf-c-form-control {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n position: relative;\n height: auto; }\n .pf-c-select__toggle .pf-c-select__toggle-clear {\n padding-right: var(--pf-c-select__toggle-clear--PaddingRight);\n padding-left: var(--pf-c-select__toggle-clear--PaddingLeft);\n margin-left: auto; }\n .pf-c-select__toggle .pf-c-select__toggle-button {\n color: var(--pf-c-select__toggle-button--Color); }\n .pf-c-select__toggle .pf-c-select__toggle-clear + .pf-c-select__toggle-button {\n padding-left: var(--pf-c-select__toggle-clear--toggle-button--PaddingLeft); }\n\n* + .pf-c-select__toggle-arrow {\n margin-right: var(--pf-c-select__toggle-arrow--MarginRight);\n margin-left: var(--pf-c-select__toggle-arrow--MarginLeft); }\n\n.pf-c-select.pf-m-top.pf-m-expanded .pf-c-select__toggle-arrow {\n transform: rotate(var(--pf-c-select__toggle-arrow--m-top--m-expanded__toggle-arrow--Rotate)); }\n\n.pf-c-select__toggle-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n\n.pf-c-select__toggle-wrapper {\n display: flex;\n flex: 1;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-start;\n min-width: 0;\n max-width: var(--pf-c-select__toggle-wrapper--MaxWidth);\n white-space: normal; }\n .pf-c-select__toggle-wrapper > :not(:last-child) {\n margin-right: var(--pf-c-select__toggle-wrapper--not-last-child--MarginRight); }\n .pf-c-select__toggle-wrapper > .pf-c-form-control {\n margin-top: calc(-1 * var(--pf-c-select__toggle-wrapper--m-typeahead--PaddingTop)); }\n .pf-c-select__toggle-wrapper .pf-c-chip-group {\n margin-top: var(--pf-c-select__toggle-wrapper--c-chip-group--MarginTop);\n margin-bottom: var(--pf-c-select__toggle-wrapper--c-chip-group--MarginBottom); }\n .pf-c-select__toggle-wrapper > .pf-c-select__toggle-typeahead:first-child {\n margin-left: calc(-1 * var(--pf-c-select__toggle--PaddingLeft)); }\n\n.pf-c-select__toggle-icon + .pf-c-select__toggle-text {\n margin-left: var(--pf-c-select__toggle-icon--toggle-text--MarginLeft); }\n\n.pf-c-select__toggle-badge {\n display: flex;\n padding-left: var(--pf-c-select__toggle-badge--PaddingLeft); }\n\n.pf-c-select__toggle-typeahead {\n flex-basis: var(--pf-c-select__toggle-typeahead--FlexBasis);\n flex-grow: 1;\n min-width: var(--pf-c-select__toggle-typeahead--MinWidth);\n background-color: var(--pf-c-select__toggle-typeahead--BackgroundColor);\n border-top: var(--pf-c-select__toggle-typeahead--BorderTop);\n border-right: var(--pf-c-select__toggle-typeahead--BorderRight);\n border-bottom-color: inherit;\n border-bottom-style: inherit;\n border-bottom-width: inherit;\n border-left: var(--pf-c-select__toggle-typeahead--BorderLeft);\n flex-shrink: 0; }\n .pf-c-select__toggle-typeahead:focus {\n padding-bottom: var(--pf-c-select__toggle-typeahead--focus--PaddingBottom); }\n\n.pf-c-select__menu {\n position: absolute;\n top: var(--pf-c-select__menu--Top);\n z-index: var(--pf-c-select__menu--ZIndex);\n min-width: 100%;\n padding-top: var(--pf-c-select__menu--PaddingTop);\n padding-bottom: var(--pf-c-select__menu--PaddingBottom);\n background-color: var(--pf-c-select__menu--BackgroundColor);\n background-clip: padding-box;\n box-shadow: var(--pf-c-select__menu--BoxShadow); }\n .pf-c-select__menu.pf-m-align-right {\n right: 0; }\n .pf-c-select.pf-m-top .pf-c-select__menu {\n top: 0;\n transform: translateY(var(--pf-c-select__menu--m-top--TranslateY)); }\n\n.pf-c-select__menu-fieldset {\n border: 0; }\n\n.pf-c-select__menu-wrapper {\n display: flex; }\n .pf-c-select__menu-wrapper.pf-m-favorite .pf-c-select__menu-item.pf-m-favorite-action {\n --pf-c-select__menu-item--Color: var(--pf-c-select__menu-wrapper--m-favorite__menu-item--m-favorite-action--Color); }\n\n.pf-c-select__menu-item {\n position: relative;\n width: var(--pf-c-select__menu-item--Width);\n padding: var(--pf-c-select__menu-item--PaddingTop) var(--pf-c-select__menu-item--PaddingRight) var(--pf-c-select__menu-item--PaddingBottom) var(--pf-c-select__menu-item--PaddingLeft);\n font-size: var(--pf-c-select__menu-item--FontSize);\n font-weight: var(--pf-c-select__menu-item--FontWeight);\n line-height: var(--pf-c-select__menu-item--LineHeight);\n color: var(--pf-c-select__menu-item--Color);\n text-align: left;\n white-space: nowrap;\n background-color: transparent;\n border: none; }\n .pf-c-select__menu-item:hover, .pf-c-select__menu-item:focus, .pf-c-select__menu-item.pf-m-focus {\n --pf-c-select__menu-item--m-action--Color: var(--pf-c-select__menu-item--hover__menu-item--m-action--Color);\n text-decoration: none; }\n .pf-c-select__menu-wrapper:hover, .pf-c-select__menu-item:hover {\n background-color: var(--pf-c-select__menu-item--hover--BackgroundColor); }\n .pf-c-select__menu-wrapper:focus-within,\n .pf-c-select__menu-wrapper.pf-m-focus, .pf-c-select__menu-item:focus, .pf-c-select__menu-item.pf-m-focus {\n position: relative;\n background-color: var(--pf-c-select__menu-item--focus--BackgroundColor); }\n .pf-c-select__menu-item.pf-m-link {\n --pf-c-select__menu-item--PaddingRight: 0;\n --pf-c-select__menu-item-main--PaddingRight: 0;\n --pf-c-select__menu-item-description--PaddingRight: 0;\n --pf-c-select__menu-item--Width: var(--pf-c-select__menu-item--m-link--Width);\n --pf-c-select__menu-item--hover--BackgroundColor: var(--pf-c-select__menu-item--m-link--hover--BackgroundColor);\n --pf-c-select__menu-item--focus--BackgroundColor: var(--pf-c-select__menu-item--m-link--focus--BackgroundColor);\n flex-grow: 1; }\n .pf-c-select__menu-item.pf-m-action {\n --pf-c-select__menu-item--Color: var(--pf-c-select__menu-item--m-action--Color);\n --pf-c-select__menu-item--Width: var(--pf-c-select__menu-item--m-action--Width);\n --pf-c-select__menu-item--hover--BackgroundColor: var(--pf-c-select__menu-item--m-action--hover--BackgroundColor);\n --pf-c-select__menu-item--focus--BackgroundColor: var(--pf-c-select__menu-item--m-action--focus--BackgroundColor);\n display: flex;\n align-items: flex-start;\n font-size: var(--pf-c-select__menu-item--m-action--FontSize); }\n .pf-c-select__menu-item.pf-m-action:hover {\n --pf-c-select__menu-item--m-action--Color: var(--pf-c-select__menu-item--m-action--hover--Color); }\n .pf-c-select__menu-item.pf-m-action:focus {\n --pf-c-select__menu-item--m-action--Color: var(--pf-c-select__menu-item--m-action--focus--Color); }\n .pf-c-select__menu-item.pf-m-selected {\n --pf-c-select__menu-item--PaddingRight: var(--pf-c-select__menu-item--m-selected--PaddingRight);\n --pf-c-select__menu-item-main--PaddingRight: var(--pf-c-select__menu-item--m-selected__menu-item-main--PaddingRight); }\n .pf-c-select__menu-item.pf-m-description {\n white-space: normal; }\n .pf-c-select__menu-item.pf-m-description:not(.pf-c-check) {\n --pf-c-select__menu-item--PaddingRight: 0; }\n .pf-c-select__menu-item.pf-m-description .pf-c-check__label {\n white-space: nowrap; }\n .pf-c-select__menu-wrapper.pf-m-disabled, .pf-c-select__menu-item:disabled, .pf-c-select__menu-item.pf-m-disabled {\n color: var(--pf-c-select__menu-item--disabled--Color);\n pointer-events: none;\n background-color: var(--pf-c-select__menu-item--disabled--BackgroundColor); }\n\n.pf-c-select__menu-item-main {\n position: relative;\n display: block;\n padding-right: var(--pf-c-select__menu-item-main--PaddingRight);\n white-space: nowrap; }\n\n.pf-c-select__menu-item-description {\n display: block;\n padding-right: var(--pf-c-select__menu-item-description--PaddingRight);\n font-size: var(--pf-c-select__menu-item-description--FontSize);\n color: var(--pf-c-select__menu-item-description--Color); }\n\n.pf-c-select__menu-item-icon {\n position: absolute;\n top: var(--pf-c-select__menu-item-icon--Top);\n right: var(--pf-c-select__menu-item-icon--Right);\n font-size: var(--pf-c-select__menu-item-icon--FontSize);\n color: var(--pf-c-select__menu-item-icon--Color);\n transform: translateY(var(--pf-c-select__menu-item-icon--TranslateY)); }\n\n.pf-c-select__menu-item-action-icon {\n display: flex;\n align-items: center;\n min-height: var(--pf-c-select__menu-item-action-icon--MinHeight); }\n\n.pf-c-select__menu-item--match {\n font-weight: var(--pf-c-select__menu-item--match--FontWeight);\n background-color: inherit; }\n\n.pf-c-select__menu-group + .pf-c-select__menu-group {\n padding-top: var(--pf-c-select__menu-group--menu-group--PaddingTop); }\n\n.pf-c-select__menu-search {\n padding: var(--pf-c-select__menu-search--PaddingTop) var(--pf-c-select__menu-search--PaddingRight) var(--pf-c-select__menu-search--PaddingBottom) var(--pf-c-select__menu-search--PaddingLeft); }\n\n.pf-c-select__menu-group-title {\n padding-top: var(--pf-c-select__menu-group-title--PaddingTop);\n padding-right: var(--pf-c-select__menu-group-title--PaddingRight);\n padding-bottom: var(--pf-c-select__menu-group-title--PaddingBottom);\n padding-left: var(--pf-c-select__menu-group-title--PaddingLeft);\n font-size: var(--pf-c-select__menu-group-title--FontSize);\n font-weight: var(--pf-c-select__menu-group-title--FontWeight);\n color: var(--pf-c-select__menu-group-title--Color); }\n\n.pf-c-simple-list {\n --pf-c-simple-list__item-link--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-simple-list__item-link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-simple-list__item-link--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-simple-list__item-link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-simple-list__item-link--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-simple-list__item-link--Color: var(--pf-global--Color--100);\n --pf-c-simple-list__item-link--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-simple-list__item-link--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-simple-list__item-link--m-current--Color: var(--pf-global--link--Color);\n --pf-c-simple-list__item-link--m-current--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-simple-list__item-link--m-current--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-simple-list__item-link--hover--Color: var(--pf-global--link--Color);\n --pf-c-simple-list__item-link--hover--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-simple-list__item-link--focus--Color: var(--pf-global--link--Color);\n --pf-c-simple-list__item-link--focus--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-simple-list__item-link--focus--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-simple-list__item-link--active--Color: var(--pf-global--link--Color);\n --pf-c-simple-list__item-link--active--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-simple-list__item-link--active--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-simple-list__title--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-simple-list__title--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-simple-list__title--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-simple-list__title--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-simple-list__title--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-simple-list__title--Color: var(--pf-global--Color--dark-200);\n --pf-c-simple-list__title--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-simple-list__section--section--MarginTop: var(--pf-global--spacer--sm); }\n\n.pf-c-simple-list__item-link {\n display: block;\n width: 100%;\n padding: var(--pf-c-simple-list__item-link--PaddingTop) var(--pf-c-simple-list__item-link--PaddingRight) var(--pf-c-simple-list__item-link--PaddingBottom) var(--pf-c-simple-list__item-link--PaddingLeft);\n font-size: var(--pf-c-simple-list__item-link--FontSize);\n font-weight: var(--pf-c-simple-list__item-link--FontWeight);\n color: var(--pf-c-simple-list__item-link--Color);\n text-align: left;\n background-color: var(--pf-c-simple-list__item-link--BackgroundColor);\n border: none; }\n .pf-c-simple-list__item-link.pf-m-current {\n --pf-c-simple-list__item-link--FontWeight: var(--pf-c-simple-list__item-link--m-current--FontWeight);\n --pf-c-simple-list__item-link--BackgroundColor: var(--pf-c-simple-list__item-link--m-current--BackgroundColor);\n --pf-c-simple-list__item-link--Color: var(--pf-c-simple-list__item-link--m-current--Color); }\n .pf-c-simple-list__item-link:hover {\n text-decoration: none;\n --pf-c-simple-list__item-link--BackgroundColor: var(--pf-c-simple-list__item-link--hover--BackgroundColor);\n --pf-c-simple-list__item-link--Color: var(--pf-c-simple-list__item-link--hover--Color); }\n .pf-c-simple-list__item-link:focus {\n --pf-c-simple-list__item-link--FontWeight: var(--pf-c-simple-list__item-link--focus--FontWeight);\n --pf-c-simple-list__item-link--BackgroundColor: var(--pf-c-simple-list__item-link--focus--BackgroundColor);\n --pf-c-simple-list__item-link--Color: var(--pf-c-simple-list__item-link--focus--Color); }\n .pf-c-simple-list__item-link:active {\n --pf-c-simple-list__item-link--FontWeight: var(--pf-c-simple-list__item-link--active--FontWeight);\n --pf-c-simple-list__item-link--BackgroundColor: var(--pf-c-simple-list__item-link--active--BackgroundColor);\n --pf-c-simple-list__item-link--Color: var(--pf-c-simple-list__item-link--active--Color); }\n\n.pf-c-simple-list__title {\n padding: var(--pf-c-simple-list__title--PaddingTop) var(--pf-c-simple-list__title--PaddingRight) var(--pf-c-simple-list__title--PaddingBottom) var(--pf-c-simple-list__title--PaddingLeft);\n font-size: var(--pf-c-simple-list__title--FontSize);\n font-weight: var(--pf-c-simple-list__title--FontWeight);\n color: var(--pf-c-simple-list__title--Color); }\n\n.pf-c-simple-list__section + .pf-c-simple-list__section {\n margin-top: var(--pf-c-simple-list__section--section--MarginTop); }\n\n.pf-c-skeleton {\n --pf-c-skeleton--BackgroundColor: var(--pf-global--palette--black-150);\n --pf-c-skeleton--Width: auto;\n --pf-c-skeleton--Height: auto;\n --pf-c-skeleton--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-skeleton--before--PaddingBottom: 0;\n --pf-c-skeleton--before--Height: auto;\n --pf-c-skeleton--before--Content: \"\\00a0\";\n --pf-c-skeleton--after--LinearGradientAngle: 90deg;\n --pf-c-skeleton--after--LinearGradientColorStop1: rgba(237, 237, 237, 0);\n --pf-c-skeleton--after--LinearGradientColorStop2: #ededed;\n --pf-c-skeleton--after--LinearGradientColorStop3: rgba(237, 237, 237, 0);\n --pf-c-skeleton--after--TranslateX: -100%;\n --pf-c-skeleton--after--AnimationName: pf-c-skeleton-loading;\n --pf-c-skeleton--after--AnimationDuration: 2s;\n --pf-c-skeleton--after--AnimationIterationCount: infinite;\n --pf-c-skeleton--after--AnimationTimingFunction: linear;\n --pf-c-skeleton--after--AnimationDelay: .5s;\n --pf-c-skeleton--m-circle--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-skeleton--m-circle--before--PaddingBottom: 100%;\n --pf-c-skeleton--m-text-4xl--Height: calc(var(--pf-global--FontSize--4xl) * var(--pf-global--LineHeight--sm));\n --pf-c-skeleton--m-text-3xl--Height: calc(var(--pf-global--FontSize--3xl) * var(--pf-global--LineHeight--sm));\n --pf-c-skeleton--m-text-2xl--Height: calc(var(--pf-global--FontSize--2xl) * var(--pf-global--LineHeight--sm));\n --pf-c-skeleton--m-text-xl--Height: calc(var(--pf-global--FontSize--xl) * var(--pf-global--LineHeight--sm));\n --pf-c-skeleton--m-text-lg--Height: calc(var(--pf-global--FontSize--lg) * var(--pf-global--LineHeight--md));\n --pf-c-skeleton--m-text-md--Height: calc(var(--pf-global--FontSize--md) * var(--pf-global--LineHeight--md));\n --pf-c-skeleton--m-text-sm--Height: calc(var(--pf-global--FontSize--sm) * var(--pf-global--LineHeight--md));\n --pf-c-skeleton--m-width-sm--Width: 6.25rem;\n --pf-c-skeleton--m-width-md--Width: 12.5rem;\n --pf-c-skeleton--m-width-lg--Width: 18.75rem;\n --pf-c-skeleton--m-width-25--Width: 25%;\n --pf-c-skeleton--m-width-33--Width: calc(100% / 3);\n --pf-c-skeleton--m-width-50--Width: 50%;\n --pf-c-skeleton--m-width-66--Width: calc(100% / 3 * 2);\n --pf-c-skeleton--m-width-75--Width: 75%;\n --pf-c-skeleton--m-height-sm--Height: 6.25rem;\n --pf-c-skeleton--m-height-md--Height: 12.5rem;\n --pf-c-skeleton--m-height-lg--Height: 18.75rem;\n --pf-c-skeleton--m-height-25--Height: 25%;\n --pf-c-skeleton--m-height-33--Height: calc(100% / 3);\n --pf-c-skeleton--m-height-50--Height: 50%;\n --pf-c-skeleton--m-height-66--Height: calc(100% / 3 * 2);\n --pf-c-skeleton--m-height-75--Height: 75%;\n --pf-c-skeleton--m-height-100--Height: 100%;\n position: relative;\n width: var(--pf-c-skeleton--Width);\n height: var(--pf-c-skeleton--Height);\n overflow: hidden;\n background-color: var(--pf-c-skeleton--BackgroundColor);\n border-radius: var(--pf-c-skeleton--BorderRadius);\n transform: translate(0); }\n .pf-c-skeleton::before {\n display: block;\n height: var(--pf-c-skeleton--before--Height);\n padding-bottom: var(--pf-c-skeleton--before--PaddingBottom);\n content: var(--pf-c-skeleton--before--Content); }\n .pf-c-skeleton::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n display: block;\n content: \"\";\n background: linear-gradient(var(--pf-c-skeleton--after--LinearGradientAngle), var(--pf-c-skeleton--after--LinearGradientColorStop1), var(--pf-c-skeleton--after--LinearGradientColorStop2), var(--pf-c-skeleton--after--LinearGradientColorStop3));\n transform: translateX(var(--pf-c-skeleton--after--TranslateX));\n animation: var(--pf-c-skeleton--after--AnimationName) var(--pf-c-skeleton--after--AnimationDuration) var(--pf-c-skeleton--after--AnimationTimingFunction) var(--pf-c-skeleton--after--AnimationDelay) var(--pf-c-skeleton--after--AnimationIterationCount); }\n .pf-c-skeleton.pf-m-circle {\n --pf-c-skeleton--BorderRadius: var(--pf-c-skeleton--m-circle--BorderRadius); }\n .pf-c-skeleton.pf-m-square, .pf-c-skeleton.pf-m-circle {\n --pf-c-skeleton--before--Height: 0;\n --pf-c-skeleton--before--PaddingBottom: var(--pf-c-skeleton--m-circle--before--PaddingBottom); }\n .pf-c-skeleton.pf-m-width-sm {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-sm--Width); }\n .pf-c-skeleton.pf-m-width-md {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-md--Width); }\n .pf-c-skeleton.pf-m-width-lg {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-lg--Width); }\n .pf-c-skeleton.pf-m-width-25 {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-25--Width); }\n .pf-c-skeleton.pf-m-width-33 {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-33--Width); }\n .pf-c-skeleton.pf-m-width-50 {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-50--Width); }\n .pf-c-skeleton.pf-m-width-66 {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-66--Width); }\n .pf-c-skeleton.pf-m-width-75 {\n --pf-c-skeleton--Width: var(--pf-c-skeleton--m-width-75--Width); }\n .pf-c-skeleton.pf-m-height-sm {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-sm--Height); }\n .pf-c-skeleton.pf-m-height-md {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-md--Height); }\n .pf-c-skeleton.pf-m-height-lg {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-lg--Height); }\n .pf-c-skeleton.pf-m-height-25 {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-25--Height); }\n .pf-c-skeleton.pf-m-height-33 {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-33--Height); }\n .pf-c-skeleton.pf-m-height-50 {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-50--Height); }\n .pf-c-skeleton.pf-m-height-66 {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-66--Height); }\n .pf-c-skeleton.pf-m-height-75 {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-75--Height); }\n .pf-c-skeleton.pf-m-height-100 {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-height-100--Height); }\n .pf-c-skeleton.pf-m-text-4xl {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-4xl--Height); }\n .pf-c-skeleton.pf-m-text-3xl {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-3xl--Height); }\n .pf-c-skeleton.pf-m-text-2xl {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-2xl--Height); }\n .pf-c-skeleton.pf-m-text-xl {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-xl--Height); }\n .pf-c-skeleton.pf-m-text-lg {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-lg--Height); }\n .pf-c-skeleton.pf-m-text-md {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-md--Height); }\n .pf-c-skeleton.pf-m-text-sm {\n --pf-c-skeleton--Height: var(--pf-c-skeleton--m-text-sm--Height); }\n\n@keyframes pf-c-skeleton-loading {\n 0% {\n transform: translateX(-100%); }\n 60% {\n transform: translateX(100%); }\n 100% {\n transform: translateX(100%); } }\n\n.pf-c-skip-to-content {\n --pf-c-skip-to-content--Top: var(--pf-global--spacer--md);\n --pf-c-skip-to-content--ZIndex: var(--pf-global--ZIndex--2xl);\n --pf-c-skip-to-content--focus--Left: var(--pf-global--spacer--md);\n position: absolute;\n top: var(--pf-c-skip-to-content--Top);\n left: -300%;\n z-index: var(--pf-c-skip-to-content--ZIndex); }\n .pf-c-skip-to-content:focus {\n left: var(--pf-c-skip-to-content--focus--Left); }\n\n.pf-c-slider {\n --pf-c-slider__rail--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-slider__rail--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-slider__rail-track--Height: 0.25rem;\n --pf-c-slider__rail-track--before--base--BackgroundColor: var(--pf-global--BorderColor--100);\n --pf-c-slider__rail-track--before--fill--BackgroundColor: var(--pf-global--active-color--300);\n --pf-c-slider__rail-track--before--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-slider__rail-track--before--fill--BackgroundColor--gradient-stop: var(--pf-c-slider--value);\n --pf-c-slider__steps--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-slider__steps--Height: var(--pf-c-slider__steps--FontSize);\n --pf-c-slider__step-tick--Top: var(--pf-global--spacer--md);\n --pf-c-slider__step-tick--Width: 0.25rem;\n --pf-c-slider__step-tick--Height: 0.25rem;\n --pf-c-slider__step-tick--BackgroundColor: var(--pf-global--BorderColor--200);\n --pf-c-slider__step-tick--TranslateX: -50%;\n --pf-c-slider__step-tick--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-slider__step--m-active__slider-tick--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-slider__step--first-child__step-tick--TranslateX: 0;\n --pf-c-slider__step--last-child__step-tick--TranslateX: -100%;\n --pf-c-slider__step-label--TranslateX: -50%;\n --pf-c-slider__step-label--Top: calc(var(--pf-global--spacer--xl) + var(--pf-c-slider__rail-track--Height));\n --pf-c-slider__step--first-child__step-label--TranslateX: 0;\n --pf-c-slider__step--last-child__step-label--TranslateX: -100%;\n --pf-c-slider__thumb--Top: calc(var(--pf-c-slider__rail-track--Height) / 2 + var(--pf-global--spacer--md));\n --pf-c-slider__thumb--Width: 1rem;\n --pf-c-slider__thumb--Height: 1rem;\n --pf-c-slider__thumb--Left: var(--pf-c-slider--value);\n --pf-c-slider__thumb--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-slider__thumb--TranslateX: -50%;\n --pf-c-slider__thumb--TranslateY: -50%;\n --pf-c-slider__thumb--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-slider__thumb--BoxShadow--base:\n 0 0 0 2px var(--pf-global--BackgroundColor--100),\n 0 0 0 3px var(--pf-global--primary-color--100);\n --pf-c-slider__thumb--hover--BoxShadow: var(--pf-c-slider__thumb--BoxShadow--base);\n --pf-c-slider__thumb--focus--BoxShadow: var(--pf-c-slider__thumb--BoxShadow--base);\n --pf-c-slider__thumb--active--BoxShadow:\n var(--pf-c-slider__thumb--BoxShadow--base),\n 0 0 2px 5px var(--pf-global--active-color--200);\n --pf-c-slider__value--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-slider__value--c-form-control--width-base: 3.5ch;\n --pf-c-slider__value--c-form-control--width-chars: 3;\n --pf-c-slider__value--c-form-control--Width: calc(var(--pf-c-slider__value--c-form-control--width-base) + (var(--pf-c-slider__value--c-form-control--width-chars) * 1ch));\n --pf-c-slider__value--m-floating--TranslateX: -50%;\n --pf-c-slider__value--m-floating--TranslateY: -100%;\n --pf-c-slider__value--m-floating--Left: var(--pf-c-slider--value);\n --pf-c-slider__value--m-floating--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-slider__actions--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-slider__main--actions--MarginLeft: var(--pf-global--spacer--sm);\n display: flex; }\n\n.pf-c-slider__main {\n position: relative;\n flex-grow: 1; }\n\n.pf-c-slider__rail {\n padding-top: var(--pf-c-slider__rail--PaddingTop);\n padding-bottom: var(--pf-c-slider__rail--PaddingBottom); }\n\n.pf-c-slider__rail-track {\n position: relative;\n height: var(--pf-c-slider__rail-track--Height); }\n .pf-c-slider__rail-track::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n background: linear-gradient(to right, var(--pf-c-slider__rail-track--before--fill--BackgroundColor), var(--pf-c-slider__rail-track--before--fill--BackgroundColor) var(--pf-c-slider__rail-track--before--fill--BackgroundColor--gradient-stop), var(--pf-c-slider__rail-track--before--base--BackgroundColor) var(--pf-c-slider__rail-track--before--fill--BackgroundColor--gradient-stop));\n border-radius: var(--pf-c-slider__rail-track--before--BorderRadius); }\n\n.pf-c-slider__steps {\n height: var(--pf-c-slider__steps--Height);\n font-size: var(--pf-c-slider__steps--FontSize);\n line-height: 1; }\n\n.pf-c-slider__step {\n position: absolute;\n top: 0;\n left: var(--pf-c-slider__step--Left);\n content: \"\"; }\n .pf-c-slider__step.pf-m-active {\n --pf-c-slider__step-tick--BackgroundColor: var(--pf-c-slider__step--m-active__slider-tick--BackgroundColor); }\n .pf-c-slider__step:first-child {\n --pf-c-slider__step-tick--TranslateX: var(--pf-c-slider__step--first-child__step-tick--TranslateX);\n --pf-c-slider__step-label--TranslateX: var(--pf-c-slider__step--first-child__step-label--TranslateX); }\n .pf-c-slider__step:last-child {\n --pf-c-slider__step-tick--TranslateX: var(--pf-c-slider__step--last-child__step-tick--TranslateX);\n --pf-c-slider__step-label--TranslateX: var(--pf-c-slider__step--last-child__step-label--TranslateX); }\n\n.pf-c-slider__step-tick {\n position: absolute;\n top: var(--pf-c-slider__step-tick--Top);\n left: 0;\n width: var(--pf-c-slider__step-tick--Width);\n height: var(--pf-c-slider__step-tick--Height);\n background-color: var(--pf-c-slider__step-tick--BackgroundColor);\n border-radius: var(--pf-c-slider__step-tick--BorderRadius);\n transform: translateX(var(--pf-c-slider__step-tick--TranslateX)); }\n\n.pf-c-slider__step-label {\n position: absolute;\n top: var(--pf-c-slider__step-label--Top);\n transform: translateX(var(--pf-c-slider__step-label--TranslateX)); }\n\n.pf-c-slider__thumb {\n position: absolute;\n top: var(--pf-c-slider__thumb--Top);\n left: var(--pf-c-slider__thumb--Left);\n width: var(--pf-c-slider__thumb--Width);\n height: var(--pf-c-slider__thumb--Height);\n cursor: pointer;\n background-color: var(--pf-c-slider__thumb--BackgroundColor);\n border-radius: var(--pf-c-slider__thumb--BorderRadius);\n box-shadow: var(--pf-c-slider__thumb--BoxShadow);\n transform: translate(var(--pf-c-slider__thumb--TranslateX), var(--pf-c-slider__thumb--TranslateY)); }\n .pf-c-slider__thumb:hover {\n --pf-c-slider__thumb--BoxShadow: var(--pf-c-slider__thumb--hover--BoxShadow); }\n .pf-c-slider__thumb:focus {\n --pf-c-slider__thumb--BoxShadow: var(--pf-c-slider__thumb--focus--BoxShadow);\n outline: 0; }\n .pf-c-slider__thumb:active {\n --pf-c-slider__thumb--BoxShadow: var(--pf-c-slider__thumb--active--BoxShadow); }\n\n.pf-c-slider__value {\n margin-left: var(--pf-c-slider__value--MarginLeft); }\n .pf-c-slider__value.pf-m-floating {\n --pf-c-slider__value--MarginLeft: 0;\n position: absolute;\n top: 0;\n left: var(--pf-c-slider__value--m-floating--Left);\n z-index: var(--pf-c-slider__value--m-floating--ZIndex);\n transform: translate(var(--pf-c-slider__value--m-floating--TranslateX), var(--pf-c-slider__value--m-floating--TranslateY)); }\n .pf-c-slider__value.pf-m-floating .pf-c-input-group {\n align-items: center; }\n .pf-c-slider__value.pf-m-floating .pf-c-input-group__text {\n position: absolute;\n left: 100%; }\n .pf-c-slider__value .pf-c-form-control {\n width: var(--pf-c-slider__value--c-form-control--Width); }\n\n.pf-c-slider__actions {\n display: flex;\n margin-right: var(--pf-c-slider__actions--MarginRight); }\n .pf-c-slider__main ~ .pf-c-slider__actions {\n --pf-c-slider__actions--MarginRight: 0;\n margin-left: var(--pf-c-slider__main--actions--MarginLeft); }\n\n.pf-c-spinner {\n --pf-c-spinner--AnimationDuration: 1.5s;\n --pf-c-spinner--AnimationTimingFunction: cubic-bezier(.77, .005, .315, 1);\n --pf-c-spinner--diameter: var(--pf-global--icon--FontSize--xl);\n --pf-c-spinner--stroke-width-multiplier: .1;\n --pf-c-spinner--stroke-width: calc(var(--pf-c-spinner--diameter) * var(--pf-c-spinner--stroke-width-multiplier));\n --pf-c-spinner--Width: var(--pf-c-spinner--diameter);\n --pf-c-spinner--Height: var(--pf-c-spinner--diameter);\n --pf-c-spinner--Color: var(--pf-global--primary-color--100);\n --pf-c-spinner--m-sm--diameter: var(--pf-global--icon--FontSize--sm);\n --pf-c-spinner--m-md--diameter: var(--pf-global--icon--FontSize--md);\n --pf-c-spinner--m-lg--diameter: var(--pf-global--icon--FontSize--lg);\n --pf-c-spinner--m-xl--diameter: var(--pf-global--icon--FontSize--xl);\n --pf-c-spinner__clipper--Width: var(--pf-c-spinner--diameter);\n --pf-c-spinner__clipper--Height: var(--pf-c-spinner--diameter);\n --pf-c-spinner__clipper--after--BoxShadowColor: var(--pf-c-spinner--Color);\n --pf-c-spinner__clipper--after--Width: var(--pf-c-spinner--diameter);\n --pf-c-spinner__clipper--after--Height: var(--pf-c-spinner--diameter);\n --pf-c-spinner__clipper--after--BoxShadowSpreadRadius: var(--pf-c-spinner--stroke-width);\n --pf-c-spinner__lead-ball--after--BackgroundColor: var(--pf-c-spinner--Color);\n --pf-c-spinner__ball--after--Width: var(--pf-c-spinner--stroke-width);\n --pf-c-spinner__ball--after--Height: var(--pf-c-spinner--stroke-width);\n --pf-c-spinner__tail-ball--after--BackgroundColor: var(--pf-c-spinner--Color);\n position: relative;\n display: inline-block;\n width: var(--pf-c-spinner--Width);\n height: var(--pf-c-spinner--Height);\n text-align: left;\n animation: pf-animation-spinner-parent calc(var(--pf-c-spinner--AnimationDuration) * 2) var(--pf-c-spinner--AnimationTimingFunction) infinite; }\n .pf-c-spinner.pf-m-sm {\n --pf-c-spinner--diameter: var(--pf-c-spinner--m-sm--diameter); }\n .pf-c-spinner.pf-m-md {\n --pf-c-spinner--diameter: var(--pf-c-spinner--m-md--diameter); }\n .pf-c-spinner.pf-m-lg {\n --pf-c-spinner--diameter: var(--pf-c-spinner--m-lg--diameter); }\n .pf-c-spinner.pf-m-xl {\n --pf-c-spinner--diameter: var(--pf-c-spinner--m-xl--diameter); }\n\n@keyframes pf-animation-spinner-parent {\n 0% {\n transform: rotate(0deg); }\n 50% {\n transform: rotate(-540deg); }\n 100% {\n transform: rotate(-1080deg); } }\n\n.pf-c-spinner__clipper {\n position: absolute;\n width: var(--pf-c-spinner__clipper--Width);\n height: var(--pf-c-spinner__clipper--Height);\n clip-path: inset(0 0 50% 50%);\n animation: pf-animation-spinner__clipper var(--pf-c-spinner--AnimationDuration) linear infinite; }\n\n@keyframes pf-animation-spinner__clipper {\n 0% {\n transform: rotate(0deg); }\n 100% {\n transform: rotate(-270deg); } }\n\n.pf-c-spinner__clipper::after {\n position: absolute;\n width: var(--pf-c-spinner__clipper--after--Width);\n height: var(--pf-c-spinner__clipper--after--Height);\n clip-path: inset(0 0 0 50%);\n content: \"\";\n border-radius: 50%;\n box-shadow: inset 0 0 0 var(--pf-c-spinner__clipper--after--BoxShadowSpreadRadius) var(--pf-c-spinner__clipper--after--BoxShadowColor);\n animation: pf-animation-spinner__clipper-after var(--pf-c-spinner--AnimationDuration) linear infinite; }\n\n@keyframes pf-animation-spinner__clipper-after {\n 0% {\n transform: rotate(90deg); }\n 100% {\n transform: rotate(-180deg); } }\n\n.pf-c-spinner__lead-ball {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n animation: pf-animation-spinner__lead-ball var(--pf-c-spinner--AnimationDuration) linear infinite; }\n .pf-c-spinner__lead-ball::after {\n position: absolute;\n top: calc(50% - (var(--pf-c-spinner__ball--after--Height) / 2));\n right: 0;\n width: var(--pf-c-spinner__ball--after--Width);\n height: var(--pf-c-spinner__ball--after--Height);\n content: \"\";\n background-color: var(--pf-c-spinner__lead-ball--after--BackgroundColor);\n border-radius: 50%;\n transform-origin: top right; }\n\n@keyframes pf-animation-spinner__lead-ball {\n 0% {\n transform: rotate(0deg); }\n 34% {\n transform: rotate(-180deg); }\n 100% {\n transform: rotate(-360deg); } }\n\n.pf-c-spinner__tail-ball {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n animation: pf-animation-spinner__tail-ball var(--pf-c-spinner--AnimationDuration) linear infinite; }\n .pf-c-spinner__tail-ball::after {\n position: absolute;\n top: calc(50% - (var(--pf-c-spinner__ball--after--Height) / 2));\n right: 0;\n width: var(--pf-c-spinner__ball--after--Width);\n height: var(--pf-c-spinner__ball--after--Height);\n content: \"\";\n background-color: var(--pf-c-spinner__tail-ball--after--BackgroundColor);\n border-radius: 50%;\n transform-origin: top right; }\n\n@keyframes pf-animation-spinner__tail-ball {\n 0% {\n transform: rotate(0deg); }\n 67.5% {\n transform: rotate(-180deg); }\n 100% {\n transform: rotate(-360deg); } }\n\n.pf-c-switch {\n --pf-c-switch--FontSize: var(--pf-global--FontSize--md);\n --pf-c-switch__toggle-icon--FontSize: calc(var(--pf-c-switch--FontSize) * .625);\n --pf-c-switch__toggle-icon--Color: var(--pf-global--Color--light-100);\n --pf-c-switch__toggle-icon--Left: calc(var(--pf-c-switch--FontSize) * .4);\n --pf-c-switch__toggle-icon--Offset: 0.125rem;\n --pf-c-switch--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-switch--Height: calc(var(--pf-c-switch--FontSize) * var(--pf-c-switch--LineHeight));\n --pf-c-switch__input--checked__toggle--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-switch__input--checked__toggle--before--TranslateX: calc(100% + var(--pf-c-switch__toggle-icon--Offset));\n --pf-c-switch__input--checked__label--Color: var(--pf-global--Color--dark-100);\n --pf-c-switch__input--not-checked__label--Color: var(--pf-global--disabled-color--100);\n --pf-c-switch__input--disabled__label--Color: var(--pf-global--disabled-color--100);\n --pf-c-switch__input--disabled__toggle--BackgroundColor: var(--pf-global--Color--dark-200);\n --pf-c-switch__input--disabled__toggle--before--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-switch__input--focus__toggle--OutlineWidth: var(--pf-global--BorderWidth--md);\n --pf-c-switch__input--focus__toggle--OutlineOffset: var(--pf-global--spacer--sm);\n --pf-c-switch__input--focus__toggle--OutlineColor: var(--pf-global--primary-color--100);\n --pf-c-switch__toggle--Height: calc(var(--pf-c-switch--FontSize) * var(--pf-c-switch--LineHeight));\n --pf-c-switch__toggle--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-switch__toggle--BorderRadius: var(--pf-c-switch--Height);\n --pf-c-switch__toggle--before--Width: calc(var(--pf-c-switch--FontSize) - var(--pf-c-switch__toggle-icon--Offset));\n --pf-c-switch__toggle--before--Height: var(--pf-c-switch__toggle--before--Width);\n --pf-c-switch__toggle--before--Top: calc((var(--pf-c-switch--Height) - var(--pf-c-switch__toggle--before--Height)) / 2);\n --pf-c-switch__toggle--before--Left: var(--pf-c-switch__toggle--before--Top);\n --pf-c-switch__toggle--before--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-switch__toggle--before--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-switch__toggle--before--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-switch__toggle--before--Transition: transform .25s ease 0s;\n --pf-c-switch__toggle--Width: calc(var(--pf-c-switch--Height) + var(--pf-c-switch__toggle-icon--Offset) + var(--pf-c-switch__toggle--before--Width));\n --pf-c-switch__label--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-switch__label--Color: var(--pf-global--Color--dark-100);\n position: relative;\n display: inline-block;\n height: var(--pf-c-switch--Height);\n font-size: var(--pf-c-switch--FontSize);\n line-height: var(--pf-c-switch--LineHeight);\n vertical-align: middle;\n cursor: pointer; }\n\n.pf-c-switch__input {\n position: absolute;\n cursor: pointer;\n opacity: 0; }\n .pf-c-switch__input:focus ~ .pf-c-switch__toggle {\n outline: var(--pf-c-switch__input--focus__toggle--OutlineWidth) solid var(--pf-c-switch__input--focus__toggle--OutlineColor);\n outline-offset: var(--pf-c-switch__input--focus__toggle--OutlineOffset); }\n .pf-c-switch__input:checked ~ .pf-c-switch__label {\n color: var(--pf-c-switch__input--checked__label--Color); }\n .pf-c-switch__input:checked ~ .pf-c-switch__toggle {\n background-color: var(--pf-c-switch__input--checked__toggle--BackgroundColor); }\n .pf-c-switch__input:checked ~ .pf-c-switch__toggle::before {\n transform: translateX(var(--pf-c-switch__input--checked__toggle--before--TranslateX)); }\n .pf-c-switch__input:checked ~ .pf-m-off {\n display: none; }\n .pf-c-switch__input:not(:checked) ~ .pf-c-switch__label {\n color: var(--pf-c-switch__input--not-checked__label--Color); }\n .pf-c-switch__input:not(:checked) ~ .pf-c-switch__toggle .pf-c-switch__toggle-icon {\n display: none;\n visibility: hidden; }\n .pf-c-switch__input:not(:checked) ~ .pf-m-on {\n display: none; }\n .pf-c-switch__input:disabled {\n cursor: not-allowed; }\n .pf-c-switch__input:disabled ~ .pf-c-switch__label {\n color: var(--pf-c-switch__input--disabled__label--Color);\n cursor: not-allowed; }\n .pf-c-switch__input:disabled ~ .pf-c-switch__toggle {\n cursor: not-allowed;\n background-color: var(--pf-c-switch__input--disabled__toggle--BackgroundColor); }\n .pf-c-switch__input:disabled ~ .pf-c-switch__toggle::before {\n background-color: var(--pf-c-switch__input--disabled__toggle--before--BackgroundColor); }\n\n.pf-c-switch__toggle {\n position: relative;\n display: inline-block;\n width: var(--pf-c-switch__toggle--Width);\n height: var(--pf-c-switch__toggle--Height);\n background-color: var(--pf-c-switch__toggle--BackgroundColor);\n border-radius: var(--pf-c-switch__toggle--BorderRadius); }\n .pf-c-switch__toggle::before {\n position: absolute;\n top: var(--pf-c-switch__toggle--before--Top);\n left: var(--pf-c-switch__toggle--before--Left);\n display: block;\n width: var(--pf-c-switch__toggle--before--Width);\n height: var(--pf-c-switch__toggle--before--Height);\n content: \"\";\n background-color: var(--pf-c-switch__toggle--before--BackgroundColor);\n border-radius: var(--pf-c-switch__toggle--before--BorderRadius);\n box-shadow: var(--pf-c-switch__toggle--before--BoxShadow);\n transition: var(--pf-c-switch__toggle--before--Transition); }\n\n.pf-c-switch__toggle-icon {\n position: absolute;\n top: 0;\n bottom: 0;\n left: var(--pf-c-switch__toggle-icon--Left);\n display: flex;\n align-items: center;\n font-size: var(--pf-c-switch__toggle-icon--FontSize);\n color: var(--pf-c-switch__toggle-icon--Color); }\n\n.pf-c-switch__label {\n display: inline-block;\n padding-left: var(--pf-c-switch__label--PaddingLeft);\n color: var(--pf-c-switch__label--Color);\n vertical-align: top; }\n\n.pf-c-tab-content {\n --pf-c-tab-content--m-light-300: var(--pf-global--BackgroundColor--light-300); }\n .pf-c-tab-content.pf-m-light-300 {\n background-color: var(--pf-c-tab-content--m-light-300); }\n\n.pf-c-table[class*=\"pf-m-grid\"] {\n --pf-c-table--responsive--BorderColor: var(--pf-global--BorderColor--300);\n --pf-c-table--tbody--responsive--border-width--base: var(--pf-global--spacer--sm);\n --pf-c-table--tbody--after--border-width--base: var(--pf-global--BorderWidth--lg);\n --pf-c-table--tbody--after--BorderLeftWidth: 0;\n --pf-c-table--tbody--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-table-tr--responsive--border-width--base: var(--pf-global--spacer--sm);\n --pf-c-table-tr--responsive--last-child--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-table-tr--responsive--GridColumnGap: var(--pf-global--spacer--md);\n --pf-c-table-tr--responsive--MarginTop: var(--pf-global--spacer--sm);\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table-tr--responsive--xl--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table-tr--responsive--xl--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-table-tr--responsive--nested-table--PaddingTop: var(--pf-global--spacer--xl);\n --pf-c-table-tr--responsive--nested-table--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table-tr--responsive--nested-table--PaddingBottom: var(--pf-global--spacer--xl);\n --pf-c-table-tr--responsive--nested-table--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table--m-grid--cell--hidden-visible--Display: grid;\n --pf-c-table--m-grid--cell--PaddingTop: 0;\n --pf-c-table--m-grid--cell--PaddingRight: 0;\n --pf-c-table--m-grid--cell--PaddingBottom: 0;\n --pf-c-table--m-grid--cell--PaddingLeft: 0;\n --pf-c-table-td--responsive--GridColumnGap: var(--pf-global--spacer--md);\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-table--cell--responsive--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-table--cell--first-child--responsive--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-table--cell--responsive--PaddingRight: 0;\n --pf-c-table--cell--responsive--PaddingLeft: 0;\n --pf-c-table--m-compact-tr--responsive--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact-tr--responsive--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact-tr-td--responsive--PaddingTop: var(--pf-global--spacer--xs);\n --pf-c-table--m-compact-tr-td--responsive--PaddingBottom: var(--pf-global--spacer--xs);\n --pf-c-table--m-compact__action--responsive--MarginTop: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-table--m-compact__action--responsive--MarginBottom: calc(var(--pf-global--spacer--xs) * -1);\n --pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom: calc(0.375rem * -1);\n --pf-c-table__expandable-row-content--responsive--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table__expandable-row-content--responsive--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table__expandable-row-content--responsive--xl--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-table__expandable-row-content--responsive--xl--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-table__expandable-row-content--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-table__check--responsive--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-table__check--responsive--MarginTop: 0.875rem;\n --pf-c-table--m-grid__favorite--MarginTop: 0.5rem;\n --pf-c-table--m-grid__check--favorite--MarginLeft: var(--pf-global--spacer--xl);\n --pf-c-table--m-grid__action--MarginTop: 0.375rem;\n --pf-c-table__action--responsive--MarginLeft: var(--pf-global--spacer--xl);\n --pf-c-table--m-grid__favorite--action--MarginLeft: var(--pf-global--spacer--2xl);\n --pf-c-table--m-grid__check--favorite--action--MarginLeft: calc(var(--pf-c-table--m-grid__check--favorite--MarginLeft) + var(--pf-c-table--m-grid__favorite--action--MarginLeft));\n --pf-c-table__toggle__icon--Transition: .2s ease-in 0s;\n --pf-c-table__toggle--m-expanded__icon--Rotate: 180deg; }\n @media screen and (max-width: 1200px) {\n .pf-c-table[class*=\"pf-m-grid\"] {\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-c-table-tr--responsive--xl--PaddingRight);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-c-table-tr--responsive--xl--PaddingLeft); } }\n @media screen and (max-width: 1200px) {\n .pf-c-table[class*=\"pf-m-grid\"] {\n --pf-c-table__expandable-row-content--responsive--PaddingRight: var(--pf-c-table__expandable-row-content--responsive--xl--PaddingRight);\n --pf-c-table__expandable-row-content--responsive--PaddingLeft: var(--pf-c-table__expandable-row-content--responsive--xl--PaddingLeft); } }\n\n.pf-m-grid.pf-c-table {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft);\n --pf-c-table__favorite--c-button--MarginTop: auto;\n --pf-c-table__favorite--c-button--MarginRight: auto;\n --pf-c-table__favorite--c-button--MarginBottom: auto;\n --pf-c-table__favorite--c-button--MarginLeft: auto;\n display: grid;\n border: none; }\n .pf-m-grid.pf-c-table tr > * {\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: visible;\n text-overflow: clip;\n white-space: normal; }\n .pf-m-grid.pf-c-table .pf-c-table__text {\n position: relative;\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-m-grid.pf-c-table thead {\n display: none;\n visibility: hidden; }\n .pf-m-grid.pf-c-table tbody {\n display: block; }\n .pf-m-grid.pf-c-table tbody:first-of-type {\n border-top: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid.pf-c-table table.pf-m-compact > tbody {\n border-top: 0; }\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) {\n border-bottom: var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid.pf-c-table tr:last-child,\n .pf-m-grid.pf-c-table tbody:last-of-type:not(:only-of-type) > tr {\n border-bottom-width: var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth); }\n .pf-m-grid.pf-c-table tbody.pf-m-expanded {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-m-grid.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row) {\n border-bottom: 0; }\n .pf-m-grid.pf-c-table tbody.pf-m-expanded:not(:last-of-type) {\n border-bottom: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) {\n display: grid;\n grid-template-columns: 1fr;\n height: auto;\n grid-auto-columns: max-content;\n grid-column-gap: var(--pf-c-table-tr--responsive--GridColumnGap);\n padding: var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft); }\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) > * {\n padding: var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft); }\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) > *:first-child {\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--cell--first-child--responsive--PaddingTop); }\n .pf-m-grid.pf-c-table.pf-m-compact {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table--m-compact-tr--responsive--PaddingTop);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);\n --pf-c-table--cell--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);\n --pf-c-table__check--input--MarginTop: 0; }\n .pf-m-grid.pf-c-table.pf-m-compact .pf-c-table__action {\n margin-top: var(--pf-c-table--m-compact__action--responsive--MarginTop);\n margin-bottom: var(--pf-c-table--m-compact__action--responsive--MarginTop); }\n .pf-m-grid.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button {\n margin-bottom: var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom); }\n .pf-m-grid.pf-c-table .pf-c-table__icon > * {\n text-align: left; }\n .pf-m-grid.pf-c-table [data-label] {\n --pf-c-table--cell--hidden-visible--Display: var(--pf-c-table--m-grid--cell--hidden-visible--Display);\n grid-column: 1;\n grid-column-gap: var(--pf-c-table-td--responsive--GridColumnGap);\n grid-template-columns: 1fr minmax(0, 1.5fr);\n align-items: start; }\n .pf-m-grid.pf-c-table [data-label] > * {\n grid-column: 2; }\n .pf-m-grid.pf-c-table [data-label]::before {\n font-weight: bold;\n text-align: left;\n content: attr(data-label); }\n .pf-m-grid.pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: 0; }\n .pf-m-grid.pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: 0; }\n .pf-m-grid.pf-c-table .pf-c-table {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table-tr--responsive--nested-table--PaddingTop);\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-c-table-tr--responsive--nested-table--PaddingRight);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);\n border: 0; }\n .pf-m-grid.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row) + tr:not(.pf-c-table__expandable-row) {\n --pf-c-table-tr--responsive--PaddingTop: 0; }\n .pf-m-grid.pf-c-table .pf-c-table__compound-expansion-toggle {\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Top: 100%; }\n .pf-m-grid.pf-c-table tbody {\n position: relative; }\n .pf-m-grid.pf-c-table tbody::after {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: 0;\n border-left: var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor); }\n .pf-m-grid.pf-c-table tbody.pf-m-expanded {\n --pf-c-table--tbody--after--BorderLeftWidth: var(--pf-c-table--tbody--after--border-width--base); }\n .pf-m-grid.pf-c-table tbody.pf-m-expanded tbody {\n --pf-c-table--tbody--after--BorderLeftWidth: 0; }\n .pf-m-grid.pf-c-table tbody > tr > :first-child:not(.pf-c-table__check)::after {\n --pf-c-table__expandable-row--after--BorderLeftWidth: 0;\n position: static;\n width: auto;\n background-color: transparent; }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row {\n --pf-c-table--cell--responsive--PaddingTop: 0;\n --pf-c-table--cell--responsive--PaddingRight: 0;\n --pf-c-table--cell--responsive--PaddingBottom: 0;\n --pf-c-table--cell--responsive--PaddingLeft: 0;\n --pf-c-table--cell--PaddingRight: 0;\n --pf-c-table--cell--PaddingLeft: 0;\n display: block;\n max-height: var(--pf-c-table__expandable-row--MaxHeight);\n overflow-y: auto;\n border-bottom: none;\n box-shadow: none; }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row > * {\n position: static;\n display: block; }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row.pf-m-expanded {\n border-top-color: var(--pf-c-table--BorderColor); }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row > :first-child:not(.pf-c-table__check)::after {\n content: none; }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content, .pf-m-grid.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content {\n padding: 0; }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded) {\n display: none;\n visibility: hidden; }\n .pf-m-grid.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content {\n padding-right: var(--pf-c-table__expandable-row-content--responsive--PaddingRight);\n padding-left: var(--pf-c-table__expandable-row-content--responsive--PaddingLeft); }\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle,\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,\n .pf-m-grid.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action {\n width: auto;\n padding: 0; }\n .pf-m-grid.pf-c-table .pf-c-table__toggle {\n grid-row-start: 20;\n grid-column: -1;\n justify-self: end;\n padding-right: 0; }\n .pf-m-grid.pf-c-table .pf-c-table__toggle::after {\n content: none; }\n .pf-m-grid.pf-c-table .pf-c-table__button {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft); }\n .pf-m-grid.pf-c-table .pf-c-table__check,\n .pf-m-grid.pf-c-table .pf-c-table__favorite,\n .pf-m-grid.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2; }\n .pf-m-grid.pf-c-table .pf-c-table__check {\n margin-top: var(--pf-c-table__check--responsive--MarginTop);\n margin-left: var(--pf-c-table__check--responsive--MarginLeft); }\n .pf-m-grid.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite {\n margin-left: var(--pf-c-table--m-grid__check--favorite--MarginLeft); }\n .pf-m-grid.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__check--favorite--action--MarginLeft); }\n .pf-m-grid.pf-c-table .pf-c-table__check ~ .pf-c-table__action {\n margin-left: var(--pf-c-table__action--responsive--MarginLeft); }\n .pf-m-grid.pf-c-table .pf-c-table__favorite {\n margin-top: var(--pf-c-table--m-grid__favorite--MarginTop); }\n .pf-m-grid.pf-c-table .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__favorite--action--MarginLeft); }\n .pf-m-grid.pf-c-table .pf-c-table__action {\n margin-top: var(--pf-c-table--m-grid__action--MarginTop);\n text-align: right; }\n @media screen and (max-width: 576px) {\n .pf-m-grid.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2;\n margin-left: 0; } }\n .pf-m-grid.pf-c-table .pf-c-table__inline-edit-action {\n grid-column: 2;\n grid-row: 2; }\n .pf-m-grid.pf-c-table .pf-c-table__toggle-icon {\n transition: var(--pf-c-table__toggle__icon--Transition); }\n .pf-c-button.pf-m-expanded > .pf-m-grid.pf-c-table .pf-c-table__toggle-icon {\n transform: rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate)); }\n .pf-m-grid.pf-c-table .pf-m-nowrap {\n --pf-c-table--cell--Overflow: auto; }\n .pf-m-grid.pf-c-table .pf-m-fit-content {\n width: auto;\n white-space: normal; }\n .pf-m-grid.pf-c-table .pf-m-truncate {\n --pf-c-table--cell--MaxWidth: 100%; }\n .pf-m-grid.pf-c-table [class*=\"pf-m-width\"] {\n --pf-c-table--cell--Width: auto; }\n\n@media screen and (max-width: 768px) {\n .pf-m-grid-md.pf-c-table {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft);\n --pf-c-table__favorite--c-button--MarginTop: auto;\n --pf-c-table__favorite--c-button--MarginRight: auto;\n --pf-c-table__favorite--c-button--MarginBottom: auto;\n --pf-c-table__favorite--c-button--MarginLeft: auto;\n display: grid;\n border: none; }\n .pf-m-grid-md.pf-c-table tr > * {\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: visible;\n text-overflow: clip;\n white-space: normal; }\n .pf-m-grid-md.pf-c-table .pf-c-table__text {\n position: relative;\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-m-grid-md.pf-c-table thead {\n display: none;\n visibility: hidden; }\n .pf-m-grid-md.pf-c-table tbody {\n display: block; }\n .pf-m-grid-md.pf-c-table tbody:first-of-type {\n border-top: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-md.pf-c-table table.pf-m-compact > tbody {\n border-top: 0; }\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) {\n border-bottom: var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-md.pf-c-table tr:last-child,\n .pf-m-grid-md.pf-c-table tbody:last-of-type:not(:only-of-type) > tr {\n border-bottom-width: var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth); }\n .pf-m-grid-md.pf-c-table tbody.pf-m-expanded {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-m-grid-md.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row) {\n border-bottom: 0; }\n .pf-m-grid-md.pf-c-table tbody.pf-m-expanded:not(:last-of-type) {\n border-bottom: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) {\n display: grid;\n grid-template-columns: 1fr;\n height: auto;\n grid-auto-columns: max-content;\n grid-column-gap: var(--pf-c-table-tr--responsive--GridColumnGap);\n padding: var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft); }\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) > * {\n padding: var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft); }\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) > *:first-child {\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--cell--first-child--responsive--PaddingTop); }\n .pf-m-grid-md.pf-c-table.pf-m-compact {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table--m-compact-tr--responsive--PaddingTop);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);\n --pf-c-table--cell--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);\n --pf-c-table__check--input--MarginTop: 0; }\n .pf-m-grid-md.pf-c-table.pf-m-compact .pf-c-table__action {\n margin-top: var(--pf-c-table--m-compact__action--responsive--MarginTop);\n margin-bottom: var(--pf-c-table--m-compact__action--responsive--MarginTop); }\n .pf-m-grid-md.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button {\n margin-bottom: var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom); }\n .pf-m-grid-md.pf-c-table .pf-c-table__icon > * {\n text-align: left; }\n .pf-m-grid-md.pf-c-table [data-label] {\n --pf-c-table--cell--hidden-visible--Display: var(--pf-c-table--m-grid--cell--hidden-visible--Display);\n grid-column: 1;\n grid-column-gap: var(--pf-c-table-td--responsive--GridColumnGap);\n grid-template-columns: 1fr minmax(0, 1.5fr);\n align-items: start; }\n .pf-m-grid-md.pf-c-table [data-label] > * {\n grid-column: 2; }\n .pf-m-grid-md.pf-c-table [data-label]::before {\n font-weight: bold;\n text-align: left;\n content: attr(data-label); }\n .pf-m-grid-md.pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: 0; }\n .pf-m-grid-md.pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: 0; }\n .pf-m-grid-md.pf-c-table .pf-c-table {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table-tr--responsive--nested-table--PaddingTop);\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-c-table-tr--responsive--nested-table--PaddingRight);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);\n border: 0; }\n .pf-m-grid-md.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row) + tr:not(.pf-c-table__expandable-row) {\n --pf-c-table-tr--responsive--PaddingTop: 0; }\n .pf-m-grid-md.pf-c-table .pf-c-table__compound-expansion-toggle {\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Top: 100%; }\n .pf-m-grid-md.pf-c-table tbody {\n position: relative; }\n .pf-m-grid-md.pf-c-table tbody::after {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: 0;\n border-left: var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor); }\n .pf-m-grid-md.pf-c-table tbody.pf-m-expanded {\n --pf-c-table--tbody--after--BorderLeftWidth: var(--pf-c-table--tbody--after--border-width--base); }\n .pf-m-grid-md.pf-c-table tbody.pf-m-expanded tbody {\n --pf-c-table--tbody--after--BorderLeftWidth: 0; }\n .pf-m-grid-md.pf-c-table tbody > tr > :first-child:not(.pf-c-table__check)::after {\n --pf-c-table__expandable-row--after--BorderLeftWidth: 0;\n position: static;\n width: auto;\n background-color: transparent; }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row {\n --pf-c-table--cell--responsive--PaddingTop: 0;\n --pf-c-table--cell--responsive--PaddingRight: 0;\n --pf-c-table--cell--responsive--PaddingBottom: 0;\n --pf-c-table--cell--responsive--PaddingLeft: 0;\n --pf-c-table--cell--PaddingRight: 0;\n --pf-c-table--cell--PaddingLeft: 0;\n display: block;\n max-height: var(--pf-c-table__expandable-row--MaxHeight);\n overflow-y: auto;\n border-bottom: none;\n box-shadow: none; }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row > * {\n position: static;\n display: block; }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row.pf-m-expanded {\n border-top-color: var(--pf-c-table--BorderColor); }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row > :first-child:not(.pf-c-table__check)::after {\n content: none; }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content, .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content {\n padding: 0; }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded) {\n display: none;\n visibility: hidden; }\n .pf-m-grid-md.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content {\n padding-right: var(--pf-c-table__expandable-row-content--responsive--PaddingRight);\n padding-left: var(--pf-c-table__expandable-row-content--responsive--PaddingLeft); }\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle,\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,\n .pf-m-grid-md.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action {\n width: auto;\n padding: 0; }\n .pf-m-grid-md.pf-c-table .pf-c-table__toggle {\n grid-row-start: 20;\n grid-column: -1;\n justify-self: end;\n padding-right: 0; }\n .pf-m-grid-md.pf-c-table .pf-c-table__toggle::after {\n content: none; }\n .pf-m-grid-md.pf-c-table .pf-c-table__button {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft); }\n .pf-m-grid-md.pf-c-table .pf-c-table__check,\n .pf-m-grid-md.pf-c-table .pf-c-table__favorite,\n .pf-m-grid-md.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2; }\n .pf-m-grid-md.pf-c-table .pf-c-table__check {\n margin-top: var(--pf-c-table__check--responsive--MarginTop);\n margin-left: var(--pf-c-table__check--responsive--MarginLeft); }\n .pf-m-grid-md.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite {\n margin-left: var(--pf-c-table--m-grid__check--favorite--MarginLeft); }\n .pf-m-grid-md.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__check--favorite--action--MarginLeft); }\n .pf-m-grid-md.pf-c-table .pf-c-table__check ~ .pf-c-table__action {\n margin-left: var(--pf-c-table__action--responsive--MarginLeft); }\n .pf-m-grid-md.pf-c-table .pf-c-table__favorite {\n margin-top: var(--pf-c-table--m-grid__favorite--MarginTop); }\n .pf-m-grid-md.pf-c-table .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__favorite--action--MarginLeft); }\n .pf-m-grid-md.pf-c-table .pf-c-table__action {\n margin-top: var(--pf-c-table--m-grid__action--MarginTop);\n text-align: right; } }\n @media screen and (max-width: 768px) and (max-width: 576px) {\n .pf-m-grid-md.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2;\n margin-left: 0; } }\n\n@media screen and (max-width: 768px) {\n .pf-m-grid-md.pf-c-table .pf-c-table__inline-edit-action {\n grid-column: 2;\n grid-row: 2; }\n .pf-m-grid-md.pf-c-table .pf-c-table__toggle-icon {\n transition: var(--pf-c-table__toggle__icon--Transition); }\n .pf-c-button.pf-m-expanded > .pf-m-grid-md.pf-c-table .pf-c-table__toggle-icon {\n transform: rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate)); }\n .pf-m-grid-md.pf-c-table .pf-m-nowrap {\n --pf-c-table--cell--Overflow: auto; }\n .pf-m-grid-md.pf-c-table .pf-m-fit-content {\n width: auto;\n white-space: normal; }\n .pf-m-grid-md.pf-c-table .pf-m-truncate {\n --pf-c-table--cell--MaxWidth: 100%; }\n .pf-m-grid-md.pf-c-table [class*=\"pf-m-width\"] {\n --pf-c-table--cell--Width: auto; } }\n\n@media screen and (max-width: 992px) {\n .pf-m-grid-lg.pf-c-table {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft);\n --pf-c-table__favorite--c-button--MarginTop: auto;\n --pf-c-table__favorite--c-button--MarginRight: auto;\n --pf-c-table__favorite--c-button--MarginBottom: auto;\n --pf-c-table__favorite--c-button--MarginLeft: auto;\n display: grid;\n border: none; }\n .pf-m-grid-lg.pf-c-table tr > * {\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: visible;\n text-overflow: clip;\n white-space: normal; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__text {\n position: relative;\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-m-grid-lg.pf-c-table thead {\n display: none;\n visibility: hidden; }\n .pf-m-grid-lg.pf-c-table tbody {\n display: block; }\n .pf-m-grid-lg.pf-c-table tbody:first-of-type {\n border-top: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-lg.pf-c-table table.pf-m-compact > tbody {\n border-top: 0; }\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) {\n border-bottom: var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-lg.pf-c-table tr:last-child,\n .pf-m-grid-lg.pf-c-table tbody:last-of-type:not(:only-of-type) > tr {\n border-bottom-width: var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth); }\n .pf-m-grid-lg.pf-c-table tbody.pf-m-expanded {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-m-grid-lg.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row) {\n border-bottom: 0; }\n .pf-m-grid-lg.pf-c-table tbody.pf-m-expanded:not(:last-of-type) {\n border-bottom: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) {\n display: grid;\n grid-template-columns: 1fr;\n height: auto;\n grid-auto-columns: max-content;\n grid-column-gap: var(--pf-c-table-tr--responsive--GridColumnGap);\n padding: var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft); }\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) > * {\n padding: var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft); }\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) > *:first-child {\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--cell--first-child--responsive--PaddingTop); }\n .pf-m-grid-lg.pf-c-table.pf-m-compact {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table--m-compact-tr--responsive--PaddingTop);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);\n --pf-c-table--cell--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);\n --pf-c-table__check--input--MarginTop: 0; }\n .pf-m-grid-lg.pf-c-table.pf-m-compact .pf-c-table__action {\n margin-top: var(--pf-c-table--m-compact__action--responsive--MarginTop);\n margin-bottom: var(--pf-c-table--m-compact__action--responsive--MarginTop); }\n .pf-m-grid-lg.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button {\n margin-bottom: var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__icon > * {\n text-align: left; }\n .pf-m-grid-lg.pf-c-table [data-label] {\n --pf-c-table--cell--hidden-visible--Display: var(--pf-c-table--m-grid--cell--hidden-visible--Display);\n grid-column: 1;\n grid-column-gap: var(--pf-c-table-td--responsive--GridColumnGap);\n grid-template-columns: 1fr minmax(0, 1.5fr);\n align-items: start; }\n .pf-m-grid-lg.pf-c-table [data-label] > * {\n grid-column: 2; }\n .pf-m-grid-lg.pf-c-table [data-label]::before {\n font-weight: bold;\n text-align: left;\n content: attr(data-label); }\n .pf-m-grid-lg.pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: 0; }\n .pf-m-grid-lg.pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: 0; }\n .pf-m-grid-lg.pf-c-table .pf-c-table {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table-tr--responsive--nested-table--PaddingTop);\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-c-table-tr--responsive--nested-table--PaddingRight);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);\n border: 0; }\n .pf-m-grid-lg.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row) + tr:not(.pf-c-table__expandable-row) {\n --pf-c-table-tr--responsive--PaddingTop: 0; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__compound-expansion-toggle {\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Top: 100%; }\n .pf-m-grid-lg.pf-c-table tbody {\n position: relative; }\n .pf-m-grid-lg.pf-c-table tbody::after {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: 0;\n border-left: var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor); }\n .pf-m-grid-lg.pf-c-table tbody.pf-m-expanded {\n --pf-c-table--tbody--after--BorderLeftWidth: var(--pf-c-table--tbody--after--border-width--base); }\n .pf-m-grid-lg.pf-c-table tbody.pf-m-expanded tbody {\n --pf-c-table--tbody--after--BorderLeftWidth: 0; }\n .pf-m-grid-lg.pf-c-table tbody > tr > :first-child:not(.pf-c-table__check)::after {\n --pf-c-table__expandable-row--after--BorderLeftWidth: 0;\n position: static;\n width: auto;\n background-color: transparent; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row {\n --pf-c-table--cell--responsive--PaddingTop: 0;\n --pf-c-table--cell--responsive--PaddingRight: 0;\n --pf-c-table--cell--responsive--PaddingBottom: 0;\n --pf-c-table--cell--responsive--PaddingLeft: 0;\n --pf-c-table--cell--PaddingRight: 0;\n --pf-c-table--cell--PaddingLeft: 0;\n display: block;\n max-height: var(--pf-c-table__expandable-row--MaxHeight);\n overflow-y: auto;\n border-bottom: none;\n box-shadow: none; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row > * {\n position: static;\n display: block; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row.pf-m-expanded {\n border-top-color: var(--pf-c-table--BorderColor); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row > :first-child:not(.pf-c-table__check)::after {\n content: none; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content, .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content {\n padding: 0; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded) {\n display: none;\n visibility: hidden; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content {\n padding-right: var(--pf-c-table__expandable-row-content--responsive--PaddingRight);\n padding-left: var(--pf-c-table__expandable-row-content--responsive--PaddingLeft); }\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle,\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,\n .pf-m-grid-lg.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action {\n width: auto;\n padding: 0; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__toggle {\n grid-row-start: 20;\n grid-column: -1;\n justify-self: end;\n padding-right: 0; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__toggle::after {\n content: none; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__button {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__check,\n .pf-m-grid-lg.pf-c-table .pf-c-table__favorite,\n .pf-m-grid-lg.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__check {\n margin-top: var(--pf-c-table__check--responsive--MarginTop);\n margin-left: var(--pf-c-table__check--responsive--MarginLeft); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite {\n margin-left: var(--pf-c-table--m-grid__check--favorite--MarginLeft); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__check--favorite--action--MarginLeft); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__check ~ .pf-c-table__action {\n margin-left: var(--pf-c-table__action--responsive--MarginLeft); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__favorite {\n margin-top: var(--pf-c-table--m-grid__favorite--MarginTop); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__favorite--action--MarginLeft); }\n .pf-m-grid-lg.pf-c-table .pf-c-table__action {\n margin-top: var(--pf-c-table--m-grid__action--MarginTop);\n text-align: right; } }\n @media screen and (max-width: 992px) and (max-width: 576px) {\n .pf-m-grid-lg.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2;\n margin-left: 0; } }\n\n@media screen and (max-width: 992px) {\n .pf-m-grid-lg.pf-c-table .pf-c-table__inline-edit-action {\n grid-column: 2;\n grid-row: 2; }\n .pf-m-grid-lg.pf-c-table .pf-c-table__toggle-icon {\n transition: var(--pf-c-table__toggle__icon--Transition); }\n .pf-c-button.pf-m-expanded > .pf-m-grid-lg.pf-c-table .pf-c-table__toggle-icon {\n transform: rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate)); }\n .pf-m-grid-lg.pf-c-table .pf-m-nowrap {\n --pf-c-table--cell--Overflow: auto; }\n .pf-m-grid-lg.pf-c-table .pf-m-fit-content {\n width: auto;\n white-space: normal; }\n .pf-m-grid-lg.pf-c-table .pf-m-truncate {\n --pf-c-table--cell--MaxWidth: 100%; }\n .pf-m-grid-lg.pf-c-table [class*=\"pf-m-width\"] {\n --pf-c-table--cell--Width: auto; } }\n\n@media screen and (max-width: 1200px) {\n .pf-m-grid-xl.pf-c-table {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft);\n --pf-c-table__favorite--c-button--MarginTop: auto;\n --pf-c-table__favorite--c-button--MarginRight: auto;\n --pf-c-table__favorite--c-button--MarginBottom: auto;\n --pf-c-table__favorite--c-button--MarginLeft: auto;\n display: grid;\n border: none; }\n .pf-m-grid-xl.pf-c-table tr > * {\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: visible;\n text-overflow: clip;\n white-space: normal; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__text {\n position: relative;\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-m-grid-xl.pf-c-table thead {\n display: none;\n visibility: hidden; }\n .pf-m-grid-xl.pf-c-table tbody {\n display: block; }\n .pf-m-grid-xl.pf-c-table tbody:first-of-type {\n border-top: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-xl.pf-c-table table.pf-m-compact > tbody {\n border-top: 0; }\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) {\n border-bottom: var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-xl.pf-c-table tr:last-child,\n .pf-m-grid-xl.pf-c-table tbody:last-of-type:not(:only-of-type) > tr {\n border-bottom-width: var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth); }\n .pf-m-grid-xl.pf-c-table tbody.pf-m-expanded {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-m-grid-xl.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row) {\n border-bottom: 0; }\n .pf-m-grid-xl.pf-c-table tbody.pf-m-expanded:not(:last-of-type) {\n border-bottom: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) {\n display: grid;\n grid-template-columns: 1fr;\n height: auto;\n grid-auto-columns: max-content;\n grid-column-gap: var(--pf-c-table-tr--responsive--GridColumnGap);\n padding: var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft); }\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) > * {\n padding: var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft); }\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) > *:first-child {\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--cell--first-child--responsive--PaddingTop); }\n .pf-m-grid-xl.pf-c-table.pf-m-compact {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table--m-compact-tr--responsive--PaddingTop);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);\n --pf-c-table--cell--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);\n --pf-c-table__check--input--MarginTop: 0; }\n .pf-m-grid-xl.pf-c-table.pf-m-compact .pf-c-table__action {\n margin-top: var(--pf-c-table--m-compact__action--responsive--MarginTop);\n margin-bottom: var(--pf-c-table--m-compact__action--responsive--MarginTop); }\n .pf-m-grid-xl.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button {\n margin-bottom: var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__icon > * {\n text-align: left; }\n .pf-m-grid-xl.pf-c-table [data-label] {\n --pf-c-table--cell--hidden-visible--Display: var(--pf-c-table--m-grid--cell--hidden-visible--Display);\n grid-column: 1;\n grid-column-gap: var(--pf-c-table-td--responsive--GridColumnGap);\n grid-template-columns: 1fr minmax(0, 1.5fr);\n align-items: start; }\n .pf-m-grid-xl.pf-c-table [data-label] > * {\n grid-column: 2; }\n .pf-m-grid-xl.pf-c-table [data-label]::before {\n font-weight: bold;\n text-align: left;\n content: attr(data-label); }\n .pf-m-grid-xl.pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: 0; }\n .pf-m-grid-xl.pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: 0; }\n .pf-m-grid-xl.pf-c-table .pf-c-table {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table-tr--responsive--nested-table--PaddingTop);\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-c-table-tr--responsive--nested-table--PaddingRight);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);\n border: 0; }\n .pf-m-grid-xl.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row) + tr:not(.pf-c-table__expandable-row) {\n --pf-c-table-tr--responsive--PaddingTop: 0; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__compound-expansion-toggle {\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Top: 100%; }\n .pf-m-grid-xl.pf-c-table tbody {\n position: relative; }\n .pf-m-grid-xl.pf-c-table tbody::after {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: 0;\n border-left: var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor); }\n .pf-m-grid-xl.pf-c-table tbody.pf-m-expanded {\n --pf-c-table--tbody--after--BorderLeftWidth: var(--pf-c-table--tbody--after--border-width--base); }\n .pf-m-grid-xl.pf-c-table tbody.pf-m-expanded tbody {\n --pf-c-table--tbody--after--BorderLeftWidth: 0; }\n .pf-m-grid-xl.pf-c-table tbody > tr > :first-child:not(.pf-c-table__check)::after {\n --pf-c-table__expandable-row--after--BorderLeftWidth: 0;\n position: static;\n width: auto;\n background-color: transparent; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row {\n --pf-c-table--cell--responsive--PaddingTop: 0;\n --pf-c-table--cell--responsive--PaddingRight: 0;\n --pf-c-table--cell--responsive--PaddingBottom: 0;\n --pf-c-table--cell--responsive--PaddingLeft: 0;\n --pf-c-table--cell--PaddingRight: 0;\n --pf-c-table--cell--PaddingLeft: 0;\n display: block;\n max-height: var(--pf-c-table__expandable-row--MaxHeight);\n overflow-y: auto;\n border-bottom: none;\n box-shadow: none; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row > * {\n position: static;\n display: block; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row.pf-m-expanded {\n border-top-color: var(--pf-c-table--BorderColor); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row > :first-child:not(.pf-c-table__check)::after {\n content: none; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content, .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content {\n padding: 0; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded) {\n display: none;\n visibility: hidden; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content {\n padding-right: var(--pf-c-table__expandable-row-content--responsive--PaddingRight);\n padding-left: var(--pf-c-table__expandable-row-content--responsive--PaddingLeft); }\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle,\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,\n .pf-m-grid-xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action {\n width: auto;\n padding: 0; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__toggle {\n grid-row-start: 20;\n grid-column: -1;\n justify-self: end;\n padding-right: 0; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__toggle::after {\n content: none; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__button {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__check,\n .pf-m-grid-xl.pf-c-table .pf-c-table__favorite,\n .pf-m-grid-xl.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__check {\n margin-top: var(--pf-c-table__check--responsive--MarginTop);\n margin-left: var(--pf-c-table__check--responsive--MarginLeft); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite {\n margin-left: var(--pf-c-table--m-grid__check--favorite--MarginLeft); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__check--favorite--action--MarginLeft); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__check ~ .pf-c-table__action {\n margin-left: var(--pf-c-table__action--responsive--MarginLeft); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__favorite {\n margin-top: var(--pf-c-table--m-grid__favorite--MarginTop); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__favorite--action--MarginLeft); }\n .pf-m-grid-xl.pf-c-table .pf-c-table__action {\n margin-top: var(--pf-c-table--m-grid__action--MarginTop);\n text-align: right; } }\n @media screen and (max-width: 1200px) and (max-width: 576px) {\n .pf-m-grid-xl.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2;\n margin-left: 0; } }\n\n@media screen and (max-width: 1200px) {\n .pf-m-grid-xl.pf-c-table .pf-c-table__inline-edit-action {\n grid-column: 2;\n grid-row: 2; }\n .pf-m-grid-xl.pf-c-table .pf-c-table__toggle-icon {\n transition: var(--pf-c-table__toggle__icon--Transition); }\n .pf-c-button.pf-m-expanded > .pf-m-grid-xl.pf-c-table .pf-c-table__toggle-icon {\n transform: rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate)); }\n .pf-m-grid-xl.pf-c-table .pf-m-nowrap {\n --pf-c-table--cell--Overflow: auto; }\n .pf-m-grid-xl.pf-c-table .pf-m-fit-content {\n width: auto;\n white-space: normal; }\n .pf-m-grid-xl.pf-c-table .pf-m-truncate {\n --pf-c-table--cell--MaxWidth: 100%; }\n .pf-m-grid-xl.pf-c-table [class*=\"pf-m-width\"] {\n --pf-c-table--cell--Width: auto; } }\n\n@media screen and (max-width: 1450px) {\n .pf-m-grid-2xl.pf-c-table {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft);\n --pf-c-table__favorite--c-button--MarginTop: auto;\n --pf-c-table__favorite--c-button--MarginRight: auto;\n --pf-c-table__favorite--c-button--MarginBottom: auto;\n --pf-c-table__favorite--c-button--MarginLeft: auto;\n display: grid;\n border: none; }\n .pf-m-grid-2xl.pf-c-table tr > * {\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: visible;\n text-overflow: clip;\n white-space: normal; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__text {\n position: relative;\n width: auto;\n min-width: 0;\n max-width: none;\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-m-grid-2xl.pf-c-table thead {\n display: none;\n visibility: hidden; }\n .pf-m-grid-2xl.pf-c-table tbody {\n display: block; }\n .pf-m-grid-2xl.pf-c-table tbody:first-of-type {\n border-top: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-2xl.pf-c-table table.pf-m-compact > tbody {\n border-top: 0; }\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) {\n border-bottom: var(--pf-c-table-tr--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-2xl.pf-c-table tr:last-child,\n .pf-m-grid-2xl.pf-c-table tbody:last-of-type:not(:only-of-type) > tr {\n border-bottom-width: var(--pf-c-table-tr--responsive--last-child--BorderBottomWidth); }\n .pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded tr:not(.pf-c-table__expandable-row) {\n border-bottom: 0; }\n .pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded:not(:last-of-type) {\n border-bottom: var(--pf-c-table--tbody--responsive--border-width--base) solid var(--pf-c-table--responsive--BorderColor); }\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) {\n display: grid;\n grid-template-columns: 1fr;\n height: auto;\n grid-auto-columns: max-content;\n grid-column-gap: var(--pf-c-table-tr--responsive--GridColumnGap);\n padding: var(--pf-c-table-tr--responsive--PaddingTop) var(--pf-c-table-tr--responsive--PaddingRight) var(--pf-c-table-tr--responsive--PaddingBottom) var(--pf-c-table-tr--responsive--PaddingLeft); }\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) > * {\n padding: var(--pf-c-table--cell--responsive--PaddingTop) var(--pf-c-table--cell--responsive--PaddingRight) var(--pf-c-table--cell--responsive--PaddingBottom) var(--pf-c-table--cell--responsive--PaddingLeft); }\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) > *:first-child {\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--cell--first-child--responsive--PaddingTop); }\n .pf-m-grid-2xl.pf-c-table.pf-m-compact {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table--m-compact-tr--responsive--PaddingTop);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr--responsive--PaddingBottom);\n --pf-c-table--cell--responsive--PaddingTop: var(--pf-c-table--m-compact-tr-td--responsive--PaddingTop);\n --pf-c-table--cell--responsive--PaddingBottom: var(--pf-c-table--m-compact-tr-td--responsive--PaddingBottom);\n --pf-c-table__check--input--MarginTop: 0; }\n .pf-m-grid-2xl.pf-c-table.pf-m-compact .pf-c-table__action {\n margin-top: var(--pf-c-table--m-compact__action--responsive--MarginTop);\n margin-bottom: var(--pf-c-table--m-compact__action--responsive--MarginTop); }\n .pf-m-grid-2xl.pf-c-table.pf-m-compact .pf-c-table__toggle .pf-c-button {\n margin-bottom: var(--pf-c-table--m-compact__toggle--c-button--responsive--MarginBottom); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__icon > * {\n text-align: left; }\n .pf-m-grid-2xl.pf-c-table [data-label] {\n --pf-c-table--cell--hidden-visible--Display: var(--pf-c-table--m-grid--cell--hidden-visible--Display);\n grid-column: 1;\n grid-column-gap: var(--pf-c-table-td--responsive--GridColumnGap);\n grid-template-columns: 1fr minmax(0, 1.5fr);\n align-items: start; }\n .pf-m-grid-2xl.pf-c-table [data-label] > * {\n grid-column: 2; }\n .pf-m-grid-2xl.pf-c-table [data-label]::before {\n font-weight: bold;\n text-align: left;\n content: attr(data-label); }\n .pf-m-grid-2xl.pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: 0; }\n .pf-m-grid-2xl.pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: 0; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table {\n --pf-c-table-tr--responsive--PaddingTop: var(--pf-c-table-tr--responsive--nested-table--PaddingTop);\n --pf-c-table-tr--responsive--PaddingRight: var(--pf-c-table-tr--responsive--nested-table--PaddingRight);\n --pf-c-table-tr--responsive--PaddingBottom: var(--pf-c-table-tr--responsive--nested-table--PaddingBottom);\n --pf-c-table-tr--responsive--PaddingLeft: var(--pf-c-table-tr--responsive--nested-table--PaddingLeft);\n border: 0; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table tr:not(.pf-c-table__expandable-row) + tr:not(.pf-c-table__expandable-row) {\n --pf-c-table-tr--responsive--PaddingTop: 0; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__compound-expansion-toggle {\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Top: 100%; }\n .pf-m-grid-2xl.pf-c-table tbody {\n position: relative; }\n .pf-m-grid-2xl.pf-c-table tbody::after {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border: 0;\n border-left: var(--pf-c-table--tbody--after--BorderLeftWidth) solid var(--pf-c-table--tbody--after--BorderColor); }\n .pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded {\n --pf-c-table--tbody--after--BorderLeftWidth: var(--pf-c-table--tbody--after--border-width--base); }\n .pf-m-grid-2xl.pf-c-table tbody.pf-m-expanded tbody {\n --pf-c-table--tbody--after--BorderLeftWidth: 0; }\n .pf-m-grid-2xl.pf-c-table tbody > tr > :first-child:not(.pf-c-table__check)::after {\n --pf-c-table__expandable-row--after--BorderLeftWidth: 0;\n position: static;\n width: auto;\n background-color: transparent; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row {\n --pf-c-table--cell--responsive--PaddingTop: 0;\n --pf-c-table--cell--responsive--PaddingRight: 0;\n --pf-c-table--cell--responsive--PaddingBottom: 0;\n --pf-c-table--cell--responsive--PaddingLeft: 0;\n --pf-c-table--cell--PaddingRight: 0;\n --pf-c-table--cell--PaddingLeft: 0;\n display: block;\n max-height: var(--pf-c-table__expandable-row--MaxHeight);\n overflow-y: auto;\n border-bottom: none;\n box-shadow: none; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row > * {\n position: static;\n display: block; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row.pf-m-expanded {\n border-top-color: var(--pf-c-table--BorderColor); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row > :first-child:not(.pf-c-table__check)::after {\n content: none; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content, .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content {\n padding: 0; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row:not(.pf-m-expanded) {\n display: none;\n visibility: hidden; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__expandable-row .pf-c-table__expandable-row-content {\n padding-right: var(--pf-c-table__expandable-row-content--responsive--PaddingRight);\n padding-left: var(--pf-c-table__expandable-row-content--responsive--PaddingLeft); }\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__toggle,\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__check,\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__favorite,\n .pf-m-grid-2xl.pf-c-table tr:not(.pf-c-table__expandable-row) .pf-c-table__action {\n width: auto;\n padding: 0; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__toggle {\n grid-row-start: 20;\n grid-column: -1;\n justify-self: end;\n padding-right: 0; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__toggle::after {\n content: none; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__button {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-grid--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-grid--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-grid--cell--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-grid--cell--PaddingLeft); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__check,\n .pf-m-grid-2xl.pf-c-table .pf-c-table__favorite,\n .pf-m-grid-2xl.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__check {\n margin-top: var(--pf-c-table__check--responsive--MarginTop);\n margin-left: var(--pf-c-table__check--responsive--MarginLeft); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite {\n margin-left: var(--pf-c-table--m-grid__check--favorite--MarginLeft); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__check ~ .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__check--favorite--action--MarginLeft); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__check ~ .pf-c-table__action {\n margin-left: var(--pf-c-table__action--responsive--MarginLeft); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__favorite {\n margin-top: var(--pf-c-table--m-grid__favorite--MarginTop); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__favorite ~ .pf-c-table__action {\n margin-left: var(--pf-c-table--m-grid__favorite--action--MarginLeft); }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__action {\n margin-top: var(--pf-c-table--m-grid__action--MarginTop);\n text-align: right; } }\n @media screen and (max-width: 1450px) and (max-width: 576px) {\n .pf-m-grid-2xl.pf-c-table .pf-c-table__action {\n grid-row-start: 1;\n grid-column-start: 2;\n margin-left: 0; } }\n\n@media screen and (max-width: 1450px) {\n .pf-m-grid-2xl.pf-c-table .pf-c-table__inline-edit-action {\n grid-column: 2;\n grid-row: 2; }\n .pf-m-grid-2xl.pf-c-table .pf-c-table__toggle-icon {\n transition: var(--pf-c-table__toggle__icon--Transition); }\n .pf-c-button.pf-m-expanded > .pf-m-grid-2xl.pf-c-table .pf-c-table__toggle-icon {\n transform: rotate(var(--pf-c-table__toggle--m-expanded__icon--Rotate)); }\n .pf-m-grid-2xl.pf-c-table .pf-m-nowrap {\n --pf-c-table--cell--Overflow: auto; }\n .pf-m-grid-2xl.pf-c-table .pf-m-fit-content {\n width: auto;\n white-space: normal; }\n .pf-m-grid-2xl.pf-c-table .pf-m-truncate {\n --pf-c-table--cell--MaxWidth: 100%; }\n .pf-m-grid-2xl.pf-c-table [class*=\"pf-m-width\"] {\n --pf-c-table--cell--Width: auto; } }\n\n.pf-c-table {\n --pf-c-table--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-table--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-table--border-width--base: var(--pf-global--BorderWidth--sm);\n --pf-c-table-caption--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-table-caption--Color: var(--pf-global--Color--200);\n --pf-c-table-caption--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-table-caption--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table-caption--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-table-caption--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table-caption--xl--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-table-caption--xl--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-table--thead--cell--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-table--thead--cell--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-table--tbody--cell--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-table--tbody--cell--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-table--cell--FontSize: var(--pf-global--FontSize--md);\n --pf-c-table--cell--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-table--cell--Color: var(--pf-global--Color--100);\n --pf-c-table--cell--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-table--cell--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-table--cell--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-table--cell--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-table--cell--first-last-child--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-table--cell--first-last-child--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-table--cell--first-last-child--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table--cell--first-last-child--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table--cell--MinWidth: 0;\n --pf-c-table--cell--MaxWidth: none;\n --pf-c-table--cell--Width: auto;\n --pf-c-table--cell--Overflow: visible;\n --pf-c-table--cell--TextOverflow: clip;\n --pf-c-table--cell--WhiteSpace: normal;\n --pf-c-table--cell--WordBreak: normal;\n --pf-c-table--cell--m-help--MinWidth: 11ch;\n --pf-c-table--m-truncate--cell--MaxWidth: 1px;\n --pf-c-table--m-truncate--cell--MinWidth: calc(5ch + var(--pf-c-table--cell--PaddingRight) + var(--pf-c-table--cell--PaddingLeft));\n --pf-c-table--cell--hidden-visible--Display: table-cell;\n --pf-c-table__toggle--c-button--MarginTop: calc(0.375rem * -1);\n --pf-c-table__toggle--c-button__toggle-icon--Rotate: 270deg;\n --pf-c-table__toggle--c-button__toggle-icon--Transition: .2s ease-in 0s;\n --pf-c-table__toggle--c-button--m-expanded__toggle-icon--Rotate: 360deg;\n --pf-c-table__button--BackgroundColor: transparent;\n --pf-c-table__button--Color: var(--pf-global--Color--100);\n --pf-c-table__button--hover--Color: var(--pf-global--Color--100);\n --pf-c-table__button--focus--Color: var(--pf-global--Color--100);\n --pf-c-table__button--active--Color: var(--pf-global--Color--100);\n --pf-c-table__button--OutlineOffset: calc(var(--pf-global--BorderWidth--lg) * -1);\n --pf-c-table--m-compact__toggle--PaddingTop: 0;\n --pf-c-table--m-compact__toggle--PaddingBottom: 0;\n --pf-c-table__check--input--MarginTop: 0.25rem;\n --pf-c-table__check--input--FontSize: var(--pf-global--FontSize--md);\n --pf-c-table--cell--m-favorite--Color: var(--pf-global--Color--light-300);\n --pf-c-table__favorite--c-button--Color: var(--pf-global--Color--light-300);\n --pf-c-table__favorite--c-button--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-table__favorite--c-button--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-table__favorite--c-button--MarginRight: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-table__favorite--c-button--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-table__favorite--c-button--MarginLeft: calc(var(--pf-global--spacer--md) * -1);\n --pf-c-table__favorite--m-favorited--c-button--Color: var(--pf-global--palette--gold-400);\n --pf-c-table__sort--m-favorite__button__text--Color: var(--pf-global--Color--200);\n --pf-c-table__sort--m-favorite__button--hover__text--Color: var(--pf-global--Color--100);\n --pf-c-table__sort--m-favorite__button--focus__text--Color: var(--pf-global--Color--100);\n --pf-c-table__sort--m-favorite__button--active__text--Color: var(--pf-global--Color--100);\n --pf-c-table__action--PaddingTop: 0;\n --pf-c-table__action--PaddingRight: 0;\n --pf-c-table__action--PaddingBottom: 0;\n --pf-c-table__action--PaddingLeft: 0;\n --pf-c-table__inline-edit-action--PaddingTop: 0;\n --pf-c-table__inline-edit-action--PaddingRight: 0;\n --pf-c-table__inline-edit-action--PaddingBottom: 0;\n --pf-c-table__inline-edit-action--PaddingLeft: 0;\n --pf-c-table__expandable-row--Transition: var(--pf-global--Transition);\n --pf-c-table__expandable-row--MaxHeight: 28.125rem;\n --pf-c-table__expandable-row-content--Transition: var(--pf-global--Transition);\n --pf-c-table__expandable-row-content--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-table__expandable-row-content--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-table__expandable-row--after--Top: calc(var(--pf-c-table--border-width--base) * -1);\n --pf-c-table__expandable-row--after--Bottom: calc(var(--pf-c-table--border-width--base) * -1);\n --pf-c-table__expandable-row--after--border-width--base: var(--pf-global--BorderWidth--lg);\n --pf-c-table__expandable-row--after--BorderLeftWidth: 0;\n --pf-c-table__expandable-row--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-table__icon-inline--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-table__sort--MinWidth: calc(6ch + var(--pf-c-table--cell--PaddingRight) + var(--pf-c-table--cell--PaddingLeft) + var(--pf-c-table__sort-indicator--MarginLeft));\n --pf-c-table__sort__button--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-table__sort__button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-table__sort__button--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-table__sort__button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-table__sort__button--MarginTop: calc(var(--pf-c-table__sort__button--PaddingTop) * -1);\n --pf-c-table__sort__button--MarginBottom: calc(var(--pf-c-table__sort__button--PaddingBottom) * -1);\n --pf-c-table__sort__button--MarginLeft: calc(var(--pf-c-table__sort__button--PaddingLeft) * -1);\n --pf-c-table__sort__button--Color: var(--pf-global--Color--100);\n --pf-c-table__sort--m-selected__button--Color: var(--pf-global--active-color--100);\n --pf-c-table__sort--m-help--MinWidth: 15ch;\n --pf-c-table__sort__button__text--Color: currentColor;\n --pf-c-table__sort__button--hover__text--Color: currentColor;\n --pf-c-table__sort__button--focus__text--Color: currentColor;\n --pf-c-table__sort__button--active__text--Color: currentColor;\n --pf-c-table__sort-indicator--Color: var(--pf-global--disabled-color--200);\n --pf-c-table__sort-indicator--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-table__sort--m-selected__sort-indicator--Color: var(--pf-global--active-color--100);\n --pf-c-table__sort__button--hover__sort-indicator--Color: var(--pf-global--Color--100);\n --pf-c-table__sort__button--active__sort-indicator--Color: var(--pf-global--Color--100);\n --pf-c-table__sort__button--focus__sort-indicator--Color: var(--pf-global--Color--100);\n --pf-c-table--th--m-help--MinWidth: 11ch;\n --pf-c-table__column-help--MarginLeft: var(--pf-global--spacer--xs);\n --pf-c-table__column-help--TranslateY: 0.125rem;\n --pf-c-table__column-help--c-button--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-table__column-help--c-button--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-table__column-help--c-button--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-table__column-help--c-button--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-table__compound-expansion-toggle__button--Color: var(--pf-global--active-color--100);\n --pf-c-table__compound-expansion-toggle__button--hover--Color: var(--pf-global--link--Color--hover);\n --pf-c-table__compound-expansion-toggle__button--focus--Color: var(--pf-global--link--Color--hover);\n --pf-c-table__compound-expansion-toggle__button--active--Color: var(--pf-global--link--Color--hover);\n --pf-c-table__compound-expansion-toggle__button--before--border-width--base: var(--pf-global--BorderWidth--sm);\n --pf-c-table__compound-expansion-toggle__button--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--before--Bottom: calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base) * -1);\n --pf-c-table__compound-expansion-toggle__button--before--Left: calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base) * -1);\n --pf-c-table__compound-expansion-toggle__button--after--border-width--base: var(--pf-global--BorderWidth--lg);\n --pf-c-table__compound-expansion-toggle__button--after--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-table__compound-expansion-toggle__button--after--BorderTopWidth: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Top: calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base) * -1);\n --pf-c-table__compound-expansion-toggle__button--after--Left: calc(var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base) * -1);\n --pf-c-table--m-compact-th--PaddingTop: calc(var(--pf-global--spacer--sm) + var(--pf-global--spacer--xs));\n --pf-c-table--m-compact-th--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact--cell--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact--cell--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact--cell--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact--cell--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-table--m-compact--cell--first-last-child--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-table--m-compact--cell--first-last-child--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-table--m-compact--cell--first-last-child--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table--m-compact--cell--first-last-child--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table--m-compact--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-table--m-compact__expandable-row-content--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-table--m-compact__expandable-row-content--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-table--m-compact__expandable-row-content--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-table--m-compact__expandable-row-content--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-table--nested--first-last-child--PaddingRight: var(--pf-global--spacer--3xl);\n --pf-c-table--nested--first-last-child--PaddingLeft: var(--pf-global--spacer--3xl);\n --pf-c-table__expandable-row--m-expanded--BorderBottomColor: var(--pf-global--BorderColor--100);\n color: var(--pf-global--Color--100);\n width: 100%;\n background-color: var(--pf-c-table--BackgroundColor); }\n @media screen and (max-width: 1200px) {\n .pf-c-table {\n --pf-c-table-caption--PaddingRight: var(--pf-c-table-caption--xl--PaddingRight);\n --pf-c-table-caption--PaddingLeft: var(--pf-c-table-caption--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-table {\n --pf-c-table--cell--first-last-child--PaddingRight: var(--pf-c-table--cell--first-last-child--xl--PaddingRight);\n --pf-c-table--cell--first-last-child--PaddingLeft: var(--pf-c-table--cell--first-last-child--xl--PaddingLeft);\n --pf-c-table--m-compact--cell--first-last-child--PaddingLeft: var(--pf-c-table--m-compact--cell--first-last-child--xl--PaddingLeft);\n --pf-c-table--m-compact--cell--first-last-child--PaddingRight: var(--pf-c-table--m-compact--cell--first-last-child--xl--PaddingRight); } }\n .pf-c-table.pf-m-fixed {\n table-layout: fixed; }\n .pf-c-table.pf-m-sticky-header {\n position: relative; }\n .pf-c-table.pf-m-sticky-header > thead > tr {\n border-bottom: 0; }\n .pf-c-table.pf-m-sticky-header > thead > tr > * {\n position: sticky;\n top: 0;\n z-index: var(--pf-global--ZIndex--xs);\n background: var(--pf-c-table--BackgroundColor); }\n .pf-c-table.pf-m-sticky-header > thead > tr > *::after {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-c-table tr:not(.pf-c-table__expandable-row) {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-c-table tr > * {\n --pf-hidden-visible--visible--Display: var(--pf-c-table--cell--hidden-visible--Display);\n position: relative;\n width: var(--pf-c-table--cell--Width);\n min-width: var(--pf-c-table--cell--MinWidth);\n max-width: var(--pf-c-table--cell--MaxWidth);\n padding: var(--pf-c-table--cell--PaddingTop) var(--pf-c-table--cell--PaddingRight) var(--pf-c-table--cell--PaddingBottom) var(--pf-c-table--cell--PaddingLeft);\n overflow: var(--pf-c-table--cell--Overflow);\n font-size: var(--pf-c-table--cell--FontSize);\n font-weight: var(--pf-c-table--cell--FontWeight);\n color: var(--pf-c-table--cell--Color);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n word-break: var(--pf-c-table--cell--WordBreak);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--cell--first-last-child--PaddingLeft); }\n .pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--cell--first-last-child--PaddingRight); }\n .pf-c-table tr > *.pf-m-center {\n text-align: center; }\n .pf-c-table tr > *:empty {\n width: auto;\n min-width: 0;\n padding: 0; }\n .pf-c-table tr > *.pf-m-help {\n --pf-c-table--cell--MinWidth: var(--pf-c-table--cell--m-help--MinWidth); }\n .pf-c-table tr > *.pf-m-favorite {\n --pf-c-table__button--Color: var(--pf-c-table--cell--m-favorite--Color);\n --pf-c-table__sort--MinWidth: fit-content;\n --pf-c-table--cell--MaxWidth: fit-content;\n --pf-c-table--cell--Overflow: visible; }\n .pf-c-table caption {\n padding-top: var(--pf-c-table-caption--PaddingTop);\n padding-bottom: var(--pf-c-table-caption--PaddingBottom);\n padding-left: var(--pf-c-table-caption--PaddingLeft);\n font-size: var(--pf-c-table-caption--FontSize);\n color: var(--pf-c-table-caption--Color);\n text-align: left;\n background-color: var(--pf-c-table--BackgroundColor); }\n .pf-c-table thead {\n --pf-c-table--cell--FontSize: var(--pf-c-table--thead--cell--FontSize);\n --pf-c-table--cell--FontWeight: var(--pf-c-table--thead--cell--FontWeight);\n vertical-align: bottom; }\n .pf-c-table tbody {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--tbody--cell--PaddingTop);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--tbody--cell--PaddingBottom); }\n .pf-c-table tbody > tr > * {\n overflow-wrap: break-word;\n vertical-align: baseline; }\n .pf-c-table tbody > tr > :first-child::after {\n position: absolute;\n top: var(--pf-c-table__expandable-row--after--Top);\n bottom: var(--pf-c-table__expandable-row--after--Bottom);\n left: 0;\n content: \"\";\n background-color: transparent;\n border-left: var(--pf-c-table__expandable-row--after--BorderLeftWidth) solid var(--pf-c-table__expandable-row--after--BorderColor); }\n .pf-c-table tbody .pf-c-table__check > input {\n margin-top: var(--pf-c-table__check--input--MarginTop);\n vertical-align: top; }\n .pf-c-table .pf-c-table__compound-expansion-toggle, .pf-c-table .pf-c-table__compound-expansion-toggle:first-child, .pf-c-table .pf-c-table__compound-expansion-toggle:last-child {\n padding: 0; }\n .pf-c-table .pf-c-table__sort {\n min-width: var(--pf-c-table__sort--MinWidth); }\n .pf-c-table .pf-m-help {\n min-width: var(--pf-c-table--th--m-help--MinWidth); }\n .pf-c-table thead,\n .pf-c-table .pf-m-truncate {\n --pf-c-table--cell--MinWidth: var(--pf-c-table--m-truncate--cell--MinWidth);\n --pf-c-table--cell--MaxWidth: var(--pf-c-table--m-truncate--cell--MaxWidth);\n --pf-c-table--cell--Overflow: hidden;\n --pf-c-table--cell--TextOverflow: ellipsis;\n --pf-c-table--cell--WhiteSpace: nowrap; }\n .pf-c-table .pf-m-wrap {\n --pf-c-table--cell--MinWidth: 0;\n --pf-c-table--cell--MaxWidth: none;\n --pf-c-table--cell--Overflow: visible;\n --pf-c-table--cell--TextOverflow: clip;\n --pf-c-table--cell--WhiteSpace: normal; }\n .pf-c-table .pf-m-nowrap {\n --pf-c-table--cell--MinWidth: 0;\n --pf-c-table--cell--MaxWidth: none;\n --pf-c-table--cell--Overflow: visible;\n --pf-c-table--cell--TextOverflow: clip;\n --pf-c-table--cell--WhiteSpace: nowrap; }\n .pf-c-table .pf-c-table__icon,\n .pf-c-table .pf-m-fit-content {\n --pf-c-table--cell--MinWidth: fit-content;\n --pf-c-table--cell--MaxWidth: fit-content;\n --pf-c-table--cell--Width: 1%;\n --pf-c-table--cell--Overflow: visible;\n --pf-c-table--cell--TextOverflow: clip;\n --pf-c-table--cell--WhiteSpace: nowrap; }\n .pf-c-table .pf-m-break-word {\n --pf-c-table--cell--WordBreak: break-word;\n --pf-c-table--cell--WhiteSpace: normal; }\n .pf-c-table.pf-m-no-border-rows > tbody > tr {\n border-bottom: 0; }\n .pf-c-table.pf-m-no-border-rows > tbody > tr > :first-child::after {\n border-left: 0; }\n .pf-c-table.pf-m-no-border-rows > tbody:not(.pf-m-expanded) .pf-c-table__compound-expansion-toggle .pf-c-table__button::before {\n display: none; }\n .pf-c-table.pf-m-no-border-rows > tbody.pf-m-expanded > .pf-c-table__control-row {\n border-bottom: var(--pf-c-table--border-width--base) solid var(--pf-c-table--BorderColor); }\n .pf-c-table.pf-m-no-border-rows > tbody .pf-c-table__control-row > .pf-c-table__compound-expansion-toggle:first-child > ::before {\n border-left-width: 0; }\n\n.pf-c-table__text {\n --pf-c-table--cell--MaxWidth: 100%;\n position: relative;\n display: block;\n width: var(--pf-c-table--cell--Width);\n min-width: var(--pf-c-table--cell--MinWidth);\n max-width: var(--pf-c-table--cell--MaxWidth);\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n word-break: var(--pf-c-table--cell--WordBreak);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n .pf-c-table__text.pf-m-truncate {\n --pf-c-table--cell--MinWidth: 100%; }\n .pf-c-table__text.pf-m-truncate > * {\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n\n.pf-c-table__button {\n position: static;\n width: 100%;\n padding: var(--pf-c-table--cell--PaddingTop) var(--pf-c-table--cell--PaddingRight) var(--pf-c-table--cell--PaddingBottom) var(--pf-c-table--cell--PaddingLeft);\n font-size: inherit;\n font-weight: inherit;\n color: var(--pf-c-table__button--Color);\n text-align: left;\n white-space: inherit;\n user-select: text;\n background-color: var(--pf-c-table__button--BackgroundColor);\n border: 0; }\n .pf-c-table__button::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n cursor: pointer;\n content: \"\"; }\n .pf-c-table__button:hover {\n color: var(--pf-c-table__button--hover--Color); }\n .pf-c-table__button:focus {\n color: var(--pf-c-table__button--focus--Color); }\n .pf-c-table__button:active {\n color: var(--pf-c-table__button--active--Color); }\n\n.pf-c-table__sort .pf-c-table__text,\n.pf-c-table__compound-expansion-toggle .pf-c-table__text {\n display: block;\n width: auto;\n overflow: var(--pf-c-table--cell--Overflow);\n text-overflow: var(--pf-c-table--cell--TextOverflow);\n white-space: var(--pf-c-table--cell--WhiteSpace); }\n\n.pf-c-table__sort .pf-c-table__text {\n --pf-c-table--cell--MinWidth: 0; }\n\n.pf-c-table__button-content,\n.pf-c-table__column-help {\n display: inline-grid;\n align-items: end;\n justify-content: start;\n grid-template-columns: auto max-content; }\n .pf-c-table__button-content .pf-c-table__text,\n .pf-c-table__column-help .pf-c-table__text {\n min-width: auto; }\n .pf-c-table thead.pf-m-nowrap .pf-c-table__button-content,\n .pf-c-table tr.pf-m-nowrap .pf-c-table__button-content,\n .pf-c-table th.pf-m-nowrap .pf-c-table__button-content, .pf-c-table thead.pf-m-nowrap\n .pf-c-table__column-help,\n .pf-c-table tr.pf-m-nowrap\n .pf-c-table__column-help,\n .pf-c-table th.pf-m-nowrap\n .pf-c-table__column-help {\n grid-template-columns: min-content max-content; }\n .pf-c-table thead.pf-m-fit-content .pf-c-table__button-content,\n .pf-c-table tr.pf-m-fit-content .pf-c-table__button-content,\n .pf-c-table th.pf-m-fit-content .pf-c-table__button-content, .pf-c-table thead.pf-m-fit-content\n .pf-c-table__column-help,\n .pf-c-table tr.pf-m-fit-content\n .pf-c-table__column-help,\n .pf-c-table th.pf-m-fit-content\n .pf-c-table__column-help {\n grid-template-columns: fit-content max-content; }\n .pf-c-table thead.pf-m-wrap .pf-c-table__button-content,\n .pf-c-table tr.pf-m-wrap .pf-c-table__button-content,\n .pf-c-table th.pf-m-wrap .pf-c-table__button-content,\n .pf-c-table thead.pf-m-truncate .pf-c-table__button-content,\n .pf-c-table tr.pf-m-truncate .pf-c-table__button-content,\n .pf-c-table th.pf-m-truncate .pf-c-table__button-content, .pf-c-table thead.pf-m-wrap\n .pf-c-table__column-help,\n .pf-c-table tr.pf-m-wrap\n .pf-c-table__column-help,\n .pf-c-table th.pf-m-wrap\n .pf-c-table__column-help,\n .pf-c-table thead.pf-m-truncate\n .pf-c-table__column-help,\n .pf-c-table tr.pf-m-truncate\n .pf-c-table__column-help,\n .pf-c-table th.pf-m-truncate\n .pf-c-table__column-help {\n grid-template-columns: auto max-content; }\n\n.pf-c-table .pf-c-table__toggle,\n.pf-c-table .pf-c-table__action,\n.pf-c-table .pf-c-table__inline-edit-action {\n --pf-c-table--cell--PaddingBottom: 0; }\n\n.pf-c-table .pf-c-table__check,\n.pf-c-table .pf-c-table__toggle,\n.pf-c-table .pf-c-table__action,\n.pf-c-table .pf-c-table__favorite,\n.pf-c-table th.pf-m-favorite,\n.pf-c-table .pf-c-table__inline-edit-action {\n --pf-c-table--cell--MinWidth: 0;\n --pf-c-table--cell--Width: 1%; }\n\n.pf-c-table__toggle {\n --pf-c-table--cell--PaddingRight: 0;\n --pf-c-table--cell--PaddingLeft: 0;\n vertical-align: top; }\n .pf-c-table__toggle .pf-c-button {\n margin-top: var(--pf-c-table__toggle--c-button--MarginTop); }\n .pf-c-table__toggle .pf-c-button.pf-m-expanded .pf-c-table__toggle-icon {\n transform: rotate(var(--pf-c-table__toggle--c-button--m-expanded__toggle-icon--Rotate)); }\n .pf-c-table__toggle .pf-c-table__toggle-icon {\n transition: var(--pf-c-table__toggle--c-button__toggle-icon--Transition);\n transform: rotate(var(--pf-c-table__toggle--c-button__toggle-icon--Rotate)); }\n .pf-c-table__toggle svg {\n pointer-events: none; }\n\n.pf-c-table__check {\n --pf-c-table--cell--FontSize: var(--pf-c-table__check--input--FontSize); }\n\n.pf-c-table__favorite .pf-c-button {\n --pf-c-button--m-plain--Color: var(--pf-c-table__favorite--c-button--Color);\n --pf-c-button--FontSize: var(--pf-c-table__favorite--c-button--FontSize);\n margin: var(--pf-c-table__favorite--c-button--MarginTop) var(--pf-c-table__favorite--c-button--MarginRight) var(--pf-c-table__favorite--c-button--MarginBottom) var(--pf-c-table__favorite--c-button--MarginLeft); }\n .pf-m-favorited.pf-c-table__favorite .pf-c-button {\n --pf-c-button--m-plain--Color: var(--pf-c-table__favorite--m-favorited--c-button--Color); }\n\n.pf-c-table__action,\n.pf-c-table__inline-edit-action {\n --pf-c-table--cell--PaddingTop: 0;\n --pf-c-table--cell--PaddingRight: var(--pf-c-table__action--PaddingRight);\n --pf-c-table--cell--PaddingBottom: 0;\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table__action--PaddingLeft);\n padding-top: 0;\n padding-bottom: 0;\n vertical-align: middle; }\n\n.pf-c-table__inline-edit-action {\n --pf-c-table--cell--PaddingLeft: 0;\n --pf-c-table--cell--PaddingRight: 0;\n text-align: right; }\n\n.pf-c-table__compound-expansion-toggle {\n --pf-c-table__button--Color: var(--pf-c-table__compound-expansion-toggle__button--Color);\n --pf-c-table__button--hover--Color: var(--pf-c-table__compound-expansion-toggle__button--hover--Color);\n --pf-c-table__button--focus--Color: var(--pf-c-table__compound-expansion-toggle__button--focus--Color);\n --pf-c-table__button--active--Color: var(--pf-c-table__compound-expansion-toggle__button--active--Color);\n position: relative; }\n .pf-c-table__compound-expansion-toggle.pf-m-truncate {\n overflow: visible; }\n .pf-c-table__compound-expansion-toggle .pf-c-table__button {\n min-width: 100%;\n overflow: hidden; }\n .pf-c-table__compound-expansion-toggle .pf-c-table__button:hover, .pf-c-table__compound-expansion-toggle .pf-c-table__button:focus, .pf-c-table__compound-expansion-toggle .pf-c-table__button:active {\n outline: 0; }\n .pf-c-table__compound-expansion-toggle .pf-c-table__button::before,\n .pf-c-table__compound-expansion-toggle .pf-c-table__button::after {\n position: absolute;\n right: 0;\n content: \"\";\n border-style: solid;\n border-width: 0; }\n .pf-c-table__compound-expansion-toggle .pf-c-table__button::before {\n top: 0;\n bottom: var(--pf-c-table__compound-expansion-toggle__button--before--Bottom);\n left: var(--pf-c-table__compound-expansion-toggle__button--before--Left);\n border-color: var(--pf-c-table__compound-expansion-toggle__button--before--BorderColor);\n border-right-width: var(--pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth);\n border-left-width: var(--pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth); }\n .pf-c-table__compound-expansion-toggle .pf-c-table__button::after {\n top: var(--pf-c-table__compound-expansion-toggle__button--after--Top);\n left: var(--pf-c-table__compound-expansion-toggle__button--after--Left);\n pointer-events: none;\n border-color: var(--pf-c-table__compound-expansion-toggle__button--after--BorderColor);\n border-top-width: var(--pf-c-table__compound-expansion-toggle__button--after--BorderTopWidth); }\n .pf-c-table__compound-expansion-toggle:hover, .pf-c-table__compound-expansion-toggle:focus-within, .pf-c-table__compound-expansion-toggle.pf-m-expanded {\n --pf-c-table__compound-expansion-toggle__button--before--BorderRightWidth: var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base);\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base);\n --pf-c-table__compound-expansion-toggle__button--after--BorderTopWidth: var(--pf-c-table__compound-expansion-toggle__button--after--border-width--base); }\n .pf-c-table__compound-expansion-toggle:first-child {\n --pf-c-table__compound-expansion-toggle__button--before--Left: 0;\n --pf-c-table__compound-expansion-toggle__button--after--Left: 0; }\n .pf-c-table__compound-expansion-toggle.pf-m-expanded .pf-c-table__button::before {\n border-bottom: var(--pf-c-table--BackgroundColor) solid var(--pf-c-table__compound-expansion-toggle__button--before--border-width--base); }\n .pf-c-table__compound-expansion-toggle.pf-m-expanded:first-child {\n --pf-c-table__compound-expansion-toggle__button--before--BorderLeftWidth: 0; }\n .pf-c-table__compound-expansion-toggle:focus-within {\n outline-offset: var(--pf-c-table__button--OutlineOffset); }\n @media (-webkit-min-device-pixel-ratio: 0) {\n .pf-c-table__compound-expansion-toggle:focus-within {\n outline-style: auto;\n outline-color: -webkit-focus-ring-color; } }\n\n.pf-c-table__column-help-action {\n margin-left: var(--pf-c-table__column-help--MarginLeft);\n transform: translateY(var(--pf-c-table__column-help--TranslateY)); }\n .pf-c-table__column-help-action .pf-c-button {\n --pf-c-button--PaddingRight: var(--pf-c-table__column-help--c-button--PaddingRight);\n --pf-c-button--PaddingLeft: var(--pf-c-table__column-help--c-button--PaddingLeft);\n margin-top: var(--pf-c-table__column-help--c-button--MarginTop);\n margin-bottom: var(--pf-c-table__column-help--c-button--MarginBottom);\n font-size: inherit;\n line-height: 1; }\n\n.pf-c-table__sort .pf-c-table__button {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table__sort__button--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table__sort__button--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table__sort__button--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table__sort__button--PaddingLeft);\n display: flex;\n width: auto;\n margin-top: var(--pf-c-table__sort__button--MarginTop);\n margin-bottom: var(--pf-c-table__sort__button--MarginBottom);\n margin-left: var(--pf-c-table__sort__button--MarginLeft); }\n .pf-c-table__sort .pf-c-table__button:hover {\n --pf-c-table__sort-indicator--Color: var(--pf-c-table__sort__button--hover__sort-indicator--Color);\n --pf-c-table__sort__button__text--Color: var(--pf-c-table__sort__button--hover__text--Color); }\n .pf-c-table__sort .pf-c-table__button:focus {\n --pf-c-table__sort-indicator--Color: var(--pf-c-table__sort__button--focus__sort-indicator--Color);\n --pf-c-table__sort__button__text--Color: var(--pf-c-table__sort__button--focus__text--Color); }\n .pf-c-table__sort .pf-c-table__button:active {\n --pf-c-table__sort-indicator--Color: var(--pf-c-table__sort__button--active__sort-indicator--Color);\n --pf-c-table__sort__button__text--Color: var(--pf-c-table__sort__button--active__text--Color); }\n .pf-c-table__sort .pf-c-table__button .pf-c-table__text {\n color: var(--pf-c-table__sort__button__text--Color); }\n\n.pf-c-table__sort.pf-m-selected .pf-c-table__button {\n --pf-c-table__sort-indicator--Color: var(--pf-c-table__sort--m-selected__sort-indicator--Color);\n --pf-c-table__sort__button__text--Color: var(--pf-c-table__sort--m-selected__button__text--Color);\n color: var(--pf-c-table__sort--m-selected__button--Color); }\n\n.pf-c-table__sort.pf-m-help {\n --pf-c-table--th--m-help--MinWidth: var(--pf-c-table__sort--m-help--MinWidth); }\n\n.pf-c-table__sort.pf-m-favorite {\n --pf-c-table__sort__button__text--Color: var(--pf-c-table__sort--m-favorite__button__text--Color);\n --pf-c-table__sort__button--hover__text--Color: var(--pf-c-table__sort--m-favorite__button--hover__text--Color);\n --pf-c-table__sort__button--focus__text--Color: var(--pf-c-table__sort--m-favorite__button--focus__text--Color);\n --pf-c-table__sort__button--active__text--Color: var(--pf-c-table__sort--m-favorite__button--active__text--Color);\n --pf-c-table__sort--m-selected__button__text--Color: currentColor; }\n\n.pf-c-table__sort-indicator {\n grid-column: 2;\n margin-left: var(--pf-c-table__sort-indicator--MarginLeft);\n color: var(--pf-c-table__sort-indicator--Color);\n pointer-events: none; }\n\n.pf-c-table__expandable-row {\n --pf-c-table--cell--PaddingTop: 0;\n --pf-c-table--cell--PaddingBottom: 0;\n position: relative;\n border-bottom: 0 solid transparent;\n box-shadow: 0 0 0 0 transparent; }\n .pf-c-table__expandable-row,\n .pf-c-table__expandable-row td:first-child::after {\n transition: var(--pf-c-table__expandable-row--Transition); }\n .pf-c-table__expandable-row td.pf-m-no-padding,\n .pf-c-table__expandable-row th.pf-m-no-padding {\n padding: 0 0 0 var(--pf-c-table__expandable-row--after--border-width--base); }\n .pf-c-table__expandable-row td.pf-m-no-padding .pf-c-table__expandable-row-content,\n .pf-c-table__expandable-row th.pf-m-no-padding .pf-c-table__expandable-row-content {\n padding: 0; }\n .pf-c-table__expandable-row .pf-c-table__expandable-row-content {\n padding-top: var(--pf-c-table__expandable-row-content--PaddingTop);\n padding-bottom: var(--pf-c-table__expandable-row-content--PaddingBottom); }\n .pf-c-table__expandable-row.pf-m-expanded {\n border-bottom-color: var(--pf-c-table__expandable-row--m-expanded--BorderBottomColor);\n border-bottom-width: var(--pf-c-table--border-width--base);\n box-shadow: var(--pf-c-table__expandable-row--m-expanded--BoxShadow); }\n .pf-c-table__expandable-row:not(.pf-m-expanded) {\n display: none;\n visibility: hidden; }\n\n.pf-c-table__compound-expansion-toggle.pf-m-expanded:first-child,\n.pf-c-table__expandable-row.pf-m-expanded > :first-child,\n.pf-c-table tbody.pf-m-expanded > tr > :not(.pf-c-table__compound-expansion-toggle) {\n --pf-c-table__expandable-row--after--BorderLeftWidth: var(--pf-c-table__expandable-row--after--border-width--base); }\n\n.pf-c-table .pf-c-table tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--nested--first-last-child--PaddingLeft); }\n\n.pf-c-table .pf-c-table tr > *:last-child {\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--nested--first-last-child--PaddingRight); }\n\n.pf-c-table.pf-m-compact {\n --pf-c-table--cell--FontSize: var(--pf-c-table--m-compact--FontSize);\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-compact--cell--PaddingTop);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-compact--cell--PaddingRight);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-compact--cell--PaddingBottom);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-compact--cell--PaddingLeft); }\n .pf-c-table.pf-m-compact tr {\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-compact--cell--PaddingLeft);\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-compact--cell--PaddingRight); }\n .pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row) {\n --pf-c-table--cell--FontSize: var(--pf-c-table--m-compact--FontSize);\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-compact--cell--PaddingTop);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-compact--cell--PaddingBottom); }\n .pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row) > *:first-child {\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--m-compact--cell--first-last-child--PaddingLeft); }\n .pf-c-table.pf-m-compact tr:not(.pf-c-table__expandable-row) > *:last-child {\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--m-compact--cell--first-last-child--PaddingRight); }\n .pf-c-table.pf-m-compact thead th {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-compact-th--PaddingTop);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-compact-th--PaddingBottom); }\n .pf-c-table.pf-m-compact .pf-c-table__action {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table__action--PaddingTop);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table__action--PaddingBottom);\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table__action--PaddingLeft); }\n .pf-c-table.pf-m-compact .pf-c-table__toggle {\n --pf-c-table--cell--PaddingTop: var(--pf-c-table--m-compact__toggle--PaddingTop);\n --pf-c-table--cell--PaddingBottom: var(--pf-c-table--m-compact__toggle--PaddingBottom); }\n .pf-c-table.pf-m-compact .pf-c-table__icon {\n width: auto;\n min-width: 0;\n text-align: center; }\n .pf-c-table .pf-c-table.pf-m-compact tr > *:first-child {\n --pf-c-table--cell--PaddingLeft: var(--pf-c-table--nested--first-last-child--PaddingLeft); }\n .pf-c-table .pf-c-table.pf-m-compact tr > *:last-child {\n --pf-c-table--cell--PaddingRight: var(--pf-c-table--nested--first-last-child--PaddingRight); }\n .pf-c-table.pf-m-compact .pf-c-table__expandable-row-content {\n --pf-c-table__expandable-row-content--PaddingTop: var(--pf-c-table--m-compact__expandable-row-content--PaddingTop);\n --pf-c-table__expandable-row-content--PaddingBottom: var(--pf-c-table--m-compact__expandable-row-content--PaddingBottom); }\n\n.pf-c-table__icon-inline {\n display: flex;\n align-items: center; }\n .pf-c-table__icon-inline > :not(:last-child) {\n margin-right: var(--pf-c-table__icon-inline--MarginRight); }\n\n.pf-c-table .pf-m-width-10 {\n --pf-c-table--cell--Width: 10%; }\n\n.pf-c-table .pf-m-width-15 {\n --pf-c-table--cell--Width: 15%; }\n\n.pf-c-table .pf-m-width-20 {\n --pf-c-table--cell--Width: 20%; }\n\n.pf-c-table .pf-m-width-25 {\n --pf-c-table--cell--Width: 25%; }\n\n.pf-c-table .pf-m-width-30 {\n --pf-c-table--cell--Width: 30%; }\n\n.pf-c-table .pf-m-width-35 {\n --pf-c-table--cell--Width: 35%; }\n\n.pf-c-table .pf-m-width-40 {\n --pf-c-table--cell--Width: 40%; }\n\n.pf-c-table .pf-m-width-45 {\n --pf-c-table--cell--Width: 45%; }\n\n.pf-c-table .pf-m-width-50 {\n --pf-c-table--cell--Width: 50%; }\n\n.pf-c-table .pf-m-width-60 {\n --pf-c-table--cell--Width: 60%; }\n\n.pf-c-table .pf-m-width-70 {\n --pf-c-table--cell--Width: 70%; }\n\n.pf-c-table .pf-m-width-80 {\n --pf-c-table--cell--Width: 80%; }\n\n.pf-c-table .pf-m-width-90 {\n --pf-c-table--cell--Width: 90%; }\n\n.pf-c-table .pf-m-width-100 {\n --pf-c-table--cell--Width: 100%; }\n\n.pf-c-tabs {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-tabs--before--border-width--base: var(--pf-global--BorderWidth--sm);\n --pf-c-tabs--before--BorderTopWidth: 0;\n --pf-c-tabs--before--BorderRightWidth: 0;\n --pf-c-tabs--before--BorderBottomWidth: var(--pf-c-tabs--before--border-width--base);\n --pf-c-tabs--before--BorderLeftWidth: 0;\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--MaxWidth: 15.625rem;\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-box__item--m-current--first-child__link--before--BorderLeftWidth: var(--pf-c-tabs__link--before--border-width--base);\n --pf-c-tabs--m-box__item--m-current--last-child__link--before--BorderRightWidth: var(--pf-c-tabs--before--border-width--base);\n --pf-c-tabs--m-color-scheme--light-300__link--BackgroundColor: transparent;\n --pf-c-tabs--m-color-scheme--light-300__item--m-current__link--BackgroundColor: var(--pf-global--BackgroundColor--light-300);\n --pf-c-tabs__link--Color: var(--pf-global--Color--200);\n --pf-c-tabs__link--FontSize: var(--pf-global--FontSize--md);\n --pf-c-tabs__link--BackgroundColor: transparent;\n --pf-c-tabs__link--OutlineOffset: calc(-1 * 0.375rem);\n --pf-c-tabs__link--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-tabs__link--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-tabs__link--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-tabs__link--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-tabs__item--m-current__link--Color: var(--pf-global--Color--100);\n --pf-c-tabs__item--m-current__link--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-tabs--m-vertical__link--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical__link--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-tabs--m-box__link--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-tabs--m-secondary__link--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-tabs__link--before--border-color--base: var(--pf-global--BorderColor--100);\n --pf-c-tabs__link--before--BorderRightColor: var(--pf-c-tabs__link--before--border-color--base);\n --pf-c-tabs__link--before--BorderBottomColor: var(--pf-c-tabs__link--before--border-color--base);\n --pf-c-tabs__link--before--border-width--base: var(--pf-global--BorderWidth--sm);\n --pf-c-tabs__link--before--BorderTopWidth: 0;\n --pf-c-tabs__link--before--BorderRightWidth: 0;\n --pf-c-tabs__link--before--BorderBottomWidth: 0;\n --pf-c-tabs__link--before--BorderLeftWidth: 0;\n --pf-c-tabs__link--before--Left: calc(var(--pf-c-tabs__link--before--border-width--base) * -1);\n --pf-c-tabs__link--after--Top: auto;\n --pf-c-tabs__link--after--Right: 0;\n --pf-c-tabs__link--after--Bottom: 0;\n --pf-c-tabs__link--after--BorderColor: var(--pf-global--BorderColor--light-100);\n --pf-c-tabs__link--after--BorderWidth: 0;\n --pf-c-tabs__link--after--BorderTopWidth: 0;\n --pf-c-tabs__link--after--BorderRightWidth: 0;\n --pf-c-tabs__link--after--BorderLeftWidth: 0;\n --pf-c-tabs__link--hover--after--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-tabs__link--focus--after--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-tabs__link--active--after--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-tabs__item--m-current__link--after--BorderColor: var(--pf-global--active-color--100);\n --pf-c-tabs__item--m-current__link--after--BorderWidth: var(--pf-global--BorderWidth--lg);\n --pf-c-tabs__link--child--MarginRight: var(--pf-global--spacer--md);\n --pf-c-tabs__scroll-button--Color: var(--pf-global--Color--100);\n --pf-c-tabs__scroll-button--hover--Color: var(--pf-global--active-color--100);\n --pf-c-tabs__scroll-button--disabled--Color: var(--pf-global--disabled-color--200);\n --pf-c-tabs__scroll-button--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-tabs__scroll-button--Width: var(--pf-global--spacer--2xl);\n --pf-c-tabs__scroll-button--xl--Width: var(--pf-global--spacer--3xl);\n --pf-c-tabs__scroll-button--OutlineOffset: calc(-1 * var(--pf-global--spacer--xs));\n --pf-c-tabs__scroll-button--TransitionDuration--margin: .125s;\n --pf-c-tabs__scroll-button--TransitionDuration--transform: .125s;\n --pf-c-tabs__scroll-button--TransitionDuration--opacity: .125s;\n --pf-c-tabs__scroll-button--before--BorderColor: var(--pf-c-tabs--before--BorderColor);\n --pf-c-tabs__scroll-button--before--border-width--base: var(--pf-global--BorderWidth--sm);\n --pf-c-tabs__scroll-button--before--BorderRightWidth: 0;\n --pf-c-tabs__scroll-button--before--BorderBottomWidth: var(--pf-c-tabs__scroll-button--before--border-width--base);\n --pf-c-tabs__scroll-button--before--BorderLeftWidth: 0;\n position: relative;\n display: flex;\n padding-right: var(--pf-c-tabs--inset);\n padding-left: var(--pf-c-tabs--inset);\n overflow: hidden; }\n @media screen and (min-width: 1200px) {\n .pf-c-tabs {\n --pf-c-tabs__scroll-button--Width: var(--pf-c-tabs__scroll-button--xl--Width); } }\n .pf-c-tabs::before {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n border: solid var(--pf-c-tabs--before--BorderColor);\n border-width: var(--pf-c-tabs--before--BorderTopWidth) var(--pf-c-tabs--before--BorderRightWidth) var(--pf-c-tabs--before--BorderBottomWidth) var(--pf-c-tabs--before--BorderLeftWidth); }\n .pf-c-tabs.pf-m-fill .pf-c-tabs__list {\n flex-basis: 100%; }\n .pf-c-tabs.pf-m-fill .pf-c-tabs__item {\n flex-grow: 1; }\n .pf-c-tabs.pf-m-fill .pf-c-tabs__item:first-child {\n --pf-c-tabs--m-box__item--m-current--first-child__link--before--BorderLeftWidth: 0; }\n .pf-c-tabs.pf-m-fill .pf-c-tabs__item:last-child {\n --pf-c-tabs--m-box__item--m-current--last-child__link--before--BorderRightWidth: 0; }\n .pf-c-tabs.pf-m-fill .pf-c-tabs__link {\n flex-basis: 100%;\n justify-content: center; }\n .pf-c-tabs.pf-m-scrollable .pf-c-tabs__scroll-button {\n opacity: 1; }\n .pf-c-tabs.pf-m-scrollable .pf-c-tabs__scroll-button:nth-of-type(1) {\n margin-right: 0;\n transform: translateX(0); }\n .pf-c-tabs.pf-m-scrollable .pf-c-tabs__scroll-button:nth-of-type(2) {\n margin-left: 0;\n transform: translateX(0); }\n .pf-c-tabs.pf-m-secondary, .pf-c-tabs.pf-m-no-border-bottom {\n --pf-c-tabs--before--BorderBottomWidth: 0; }\n .pf-c-tabs.pf-m-box .pf-c-tabs__link, .pf-c-tabs.pf-m-vertical .pf-c-tabs__link {\n --pf-c-tabs__link--after--BorderBottomWidth: 0; }\n .pf-c-tabs.pf-m-box {\n --pf-c-tabs__link--BackgroundColor: var(--pf-c-tabs--m-box__link--BackgroundColor);\n --pf-c-tabs__link--before--BorderBottomWidth: var(--pf-c-tabs__link--before--border-width--base);\n --pf-c-tabs__link--before--BorderRightWidth: var(--pf-c-tabs__link--before--border-width--base);\n --pf-c-tabs__link--after--Top: 0;\n --pf-c-tabs__link--after--Bottom: auto; }\n .pf-c-tabs.pf-m-box .pf-c-tabs__link {\n --pf-c-tabs__link--after--BorderTopWidth: var(--pf-c-tabs__link--after--BorderWidth); }\n .pf-c-tabs.pf-m-box .pf-c-tabs__item:last-child {\n --pf-c-tabs__link--before--BorderRightWidth: 0; }\n .pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current {\n --pf-c-tabs__link--BackgroundColor: var(--pf-c-tabs__item--m-current__link--BackgroundColor);\n --pf-c-tabs__link--before--BorderBottomColor: var(--pf-c-tabs__link--BackgroundColor); }\n .pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current:first-child .pf-c-tabs__link::before {\n border-left-width: var(--pf-c-tabs--m-box__item--m-current--first-child__link--before--BorderLeftWidth); }\n .pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current:last-child .pf-c-tabs__link::before {\n border-right-width: var(--pf-c-tabs--m-box__item--m-current--last-child__link--before--BorderRightWidth); }\n .pf-c-tabs.pf-m-box.pf-m-scrollable .pf-c-tabs__item.pf-m-current:first-child .pf-c-tabs__link::before {\n left: calc(var(--pf-c-tabs__link--before--border-width--base) * -1); }\n .pf-c-tabs.pf-m-box.pf-m-scrollable .pf-c-tabs__scroll-button:nth-of-type(2)::before {\n left: calc(var(--pf-c-tabs__link--before--border-width--base) * -1); }\n .pf-c-tabs.pf-m-box .pf-c-tabs__item.pf-m-current + .pf-c-tabs__item {\n --pf-c-tabs__link--before--Left: 0; }\n .pf-c-tabs.pf-m-box.pf-m-color-scheme--light-300 {\n --pf-c-tabs__link--BackgroundColor: var(--pf-c-tabs--m-color-scheme--light-300__link--BackgroundColor);\n --pf-c-tabs__item--m-current__link--BackgroundColor: var(--pf-c-tabs--m-color-scheme--light-300__item--m-current__link--BackgroundColor); }\n .pf-c-tabs.pf-m-vertical {\n --pf-c-tabs--inset: var(--pf-c-tabs--m-vertical--inset);\n --pf-c-tabs--before--BorderBottomWidth: 0;\n --pf-c-tabs--before--BorderLeftWidth: var(--pf-c-tabs--before--border-width--base);\n --pf-c-tabs__link--PaddingTop: var(--pf-c-tabs--m-vertical__link--PaddingTop);\n --pf-c-tabs__link--PaddingBottom: var(--pf-c-tabs--m-vertical__link--PaddingBottom);\n --pf-c-tabs__link--before--Left: 0;\n --pf-c-tabs__link--after--Top: 0;\n --pf-c-tabs__link--after--Bottom: 0;\n --pf-c-tabs__link--after--Right: auto;\n display: inline-flex;\n flex-direction: column;\n height: 100%;\n padding: 0; }\n .pf-c-tabs.pf-m-vertical::before {\n top: 0;\n right: auto; }\n .pf-c-tabs.pf-m-vertical .pf-c-tabs__list {\n flex-direction: column;\n max-width: var(--pf-c-tabs--m-vertical--MaxWidth); }\n .pf-c-tabs.pf-m-vertical .pf-c-tabs__item:first-child {\n margin-top: var(--pf-c-tabs--inset); }\n .pf-c-tabs.pf-m-vertical .pf-c-tabs__item:last-child {\n margin-bottom: var(--pf-c-tabs--inset); }\n .pf-c-tabs.pf-m-vertical .pf-c-tabs__link {\n --pf-c-tabs__link--after--BorderTopWidth: 0;\n --pf-c-tabs__link--after--BorderLeftWidth: var(--pf-c-tabs__link--after--BorderWidth);\n max-width: 100%;\n text-align: left; }\n .pf-c-tabs.pf-m-vertical .pf-c-tabs__item-text {\n max-width: 100%;\n overflow-wrap: break-word; }\n .pf-c-tabs.pf-m-box.pf-m-vertical {\n --pf-c-tabs--inset: var(--pf-c-tabs--m-vertical--m-box--inset);\n --pf-c-tabs--before--BorderLeftWidth: 0;\n --pf-c-tabs--before--BorderRightWidth: var(--pf-c-tabs--before--border-width--base); }\n .pf-c-tabs.pf-m-box.pf-m-vertical::before {\n right: 0;\n left: auto; }\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item:last-child {\n --pf-c-tabs__link--before--BorderBottomWidth: 0;\n --pf-c-tabs__link--before--BorderRightWidth: var(--pf-c-tabs__link--before--border-width--base); }\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item.pf-m-current {\n --pf-c-tabs__link--before--BorderRightColor: var(--pf-c-tabs__item--m-current__link--BackgroundColor);\n --pf-c-tabs__link--before--BorderBottomColor: var(--pf-c-tabs__link--before--border-color--base);\n --pf-c-tabs__link--before--BorderBottomWidth: var(--pf-c-tabs__link--before--border-width--base); }\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item.pf-m-current:first-child {\n --pf-c-tabs__link--before--BorderTopWidth: var(--pf-c-tabs__link--before--border-width--base); }\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item:first-child.pf-m-current {\n --pf-c-tabs__link--before--BorderTopWidth: var(--pf-c-tabs__link--before--border-width--base); }\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__link::after {\n top: calc(var(--pf-c-tabs__link--before--border-width--base) * -1); }\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item:first-child .pf-c-tabs__link::after,\n .pf-c-tabs.pf-m-box.pf-m-vertical .pf-c-tabs__item.pf-m-current + .pf-c-tabs__item .pf-c-tabs__link::after {\n top: 0; }\n .pf-c-tabs.pf-m-secondary {\n --pf-c-tabs__link--FontSize: var(--pf-c-tabs--m-secondary__link--FontSize); }\n\n.pf-c-tabs__list {\n scrollbar-width: none;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n position: relative;\n display: flex;\n max-width: 100%;\n overflow-x: auto;\n scroll-behavior: smooth;\n -webkit-overflow-scrolling: touch; }\n .pf-c-tabs__list::-webkit-scrollbar {\n display: none; }\n\n.pf-c-tabs__item {\n display: flex;\n flex: none; }\n .pf-c-tabs__item.pf-m-current {\n --pf-c-tabs__link--Color: var(--pf-c-tabs__item--m-current__link--Color);\n --pf-c-tabs__link--after--BorderColor: var(--pf-c-tabs__item--m-current__link--after--BorderColor);\n --pf-c-tabs__link--after--BorderWidth: var(--pf-c-tabs__item--m-current__link--after--BorderWidth); }\n\n.pf-c-tabs__link,\n.pf-c-tabs__scroll-button {\n border: 0; }\n\n.pf-c-tabs::before,\n.pf-c-tabs__link::before,\n.pf-c-tabs__link::after,\n.pf-c-tabs__scroll-button::before {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n content: \"\";\n border-style: solid; }\n\n.pf-c-tabs__link::before,\n.pf-c-tabs__link::after,\n.pf-c-tabs__scroll-button::before {\n top: 0; }\n\n.pf-c-tabs__link {\n --pf-c-tabs__link--after--BorderBottomWidth: var(--pf-c-tabs__link--after--BorderWidth);\n position: relative;\n display: flex;\n flex: 1;\n padding: var(--pf-c-tabs__link--PaddingTop) var(--pf-c-tabs__link--PaddingRight) var(--pf-c-tabs__link--PaddingBottom) var(--pf-c-tabs__link--PaddingLeft);\n font-size: var(--pf-c-tabs__link--FontSize);\n color: var(--pf-c-tabs__link--Color);\n text-decoration: none;\n background-color: var(--pf-c-tabs__link--BackgroundColor);\n outline-offset: var(--pf-c-tabs__link--OutlineOffset); }\n .pf-c-tabs__link::before {\n pointer-events: none;\n border-color: var(--pf-c-tabs__link--before--border-color--base);\n border-width: var(--pf-c-tabs__link--before--BorderTopWidth) var(--pf-c-tabs__link--before--BorderRightWidth) var(--pf-c-tabs__link--before--BorderBottomWidth) var(--pf-c-tabs__link--before--BorderLeftWidth);\n border-right-color: var(--pf-c-tabs__link--before--BorderRightColor);\n border-bottom-color: var(--pf-c-tabs__link--before--BorderBottomColor); }\n .pf-c-tabs__link::after {\n top: var(--pf-c-tabs__link--after--Top);\n right: var(--pf-c-tabs__link--after--Right);\n bottom: var(--pf-c-tabs__link--after--Bottom);\n left: var(--pf-c-tabs__link--before--Left);\n border-color: var(--pf-c-tabs__link--after--BorderColor);\n border-width: var(--pf-c-tabs__link--after--BorderTopWidth) var(--pf-c-tabs__link--after--BorderRightWidth) var(--pf-c-tabs__link--after--BorderBottomWidth) var(--pf-c-tabs__link--after--BorderLeftWidth); }\n .pf-c-tabs__link:hover {\n --pf-c-tabs__link--after--BorderWidth: var(--pf-c-tabs__link--hover--after--BorderWidth); }\n .pf-c-tabs__link:focus {\n --pf-c-tabs__link--after--BorderWidth: var(--pf-c-tabs__link--focus--after--BorderWidth); }\n .pf-c-tabs__link:active {\n --pf-c-tabs__link--after--BorderWidth: var(--pf-c-tabs__link--active--after--BorderWidth); }\n .pf-c-tabs__link .pf-c-tabs__item-icon,\n .pf-c-tabs__link .pf-c-tabs__item-text {\n margin-right: var(--pf-c-tabs__link--child--MarginRight); }\n .pf-c-tabs__link .pf-c-tabs__item-icon:last-child,\n .pf-c-tabs__link .pf-c-tabs__item-text:last-child {\n --pf-c-tabs__link--child--MarginRight: 0; }\n\n.pf-c-tabs__scroll-button {\n flex: none;\n width: var(--pf-c-tabs__scroll-button--Width);\n line-height: 1;\n color: var(--pf-c-tabs__scroll-button--Color);\n background-color: var(--pf-c-tabs__scroll-button--BackgroundColor);\n outline-offset: var(--pf-c-tabs__scroll-button--OutlineOffset);\n opacity: 0;\n transition: margin var(--pf-c-tabs__scroll-button--TransitionDuration--margin), transform var(--pf-c-tabs__scroll-button--TransitionDuration--transform), opacity var(--pf-c-tabs__scroll-button--TransitionDuration--opacity); }\n .pf-c-tabs__scroll-button:hover, .pf-c-tabs__scroll-button:active, .pf-c-tabs__scroll-button:focus {\n --pf-c-tabs__scroll-button--Color: var(--pf-c-tabs__scroll-button--hover--Color); }\n .pf-c-tabs__scroll-button::before {\n border-color: var(--pf-c-tabs__scroll-button--before--BorderColor);\n border-width: 0 var(--pf-c-tabs__scroll-button--before--BorderRightWidth) var(--pf-c-tabs__scroll-button--before--BorderBottomWidth) var(--pf-c-tabs__scroll-button--before--BorderLeftWidth); }\n .pf-c-tabs__scroll-button:nth-of-type(1) {\n --pf-c-tabs__scroll-button--before--BorderRightWidth: var(--pf-c-tabs__scroll-button--before--border-width--base);\n margin-right: calc(var(--pf-c-tabs__scroll-button--Width) * -1);\n transform: translateX(-100%); }\n .pf-c-tabs__scroll-button:nth-of-type(2) {\n --pf-c-tabs__scroll-button--before--BorderLeftWidth: var(--pf-c-tabs__scroll-button--before--border-width--base);\n margin-left: calc(var(--pf-c-tabs__scroll-button--Width) * -1);\n transform: translateX(100%); }\n .pf-c-tabs__scroll-button:disabled {\n --pf-c-tabs__scroll-button--Color: var(--pf-c-tabs__scroll-button--disabled--Color);\n pointer-events: none; }\n\n.pf-c-tabs.pf-m-inset-none {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--m-vertical--inset: 0;\n --pf-c-tabs--m-vertical--m-box--inset: 0; }\n\n.pf-c-tabs.pf-m-inset-sm {\n --pf-c-tabs--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--sm); }\n\n.pf-c-tabs.pf-m-inset-md {\n --pf-c-tabs--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--md); }\n\n.pf-c-tabs.pf-m-inset-lg {\n --pf-c-tabs--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg); }\n\n.pf-c-tabs.pf-m-inset-xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl); }\n\n.pf-c-tabs.pf-m-inset-2xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--2xl); }\n\n@media (min-width: 576px) {\n .pf-c-tabs.pf-m-inset-none-on-sm {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--m-vertical--inset: 0;\n --pf-c-tabs--m-vertical--m-box--inset: 0; }\n .pf-c-tabs.pf-m-inset-sm-on-sm {\n --pf-c-tabs--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--sm); }\n .pf-c-tabs.pf-m-inset-md-on-sm {\n --pf-c-tabs--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--md); }\n .pf-c-tabs.pf-m-inset-lg-on-sm {\n --pf-c-tabs--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg); }\n .pf-c-tabs.pf-m-inset-xl-on-sm {\n --pf-c-tabs--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl); }\n .pf-c-tabs.pf-m-inset-2xl-on-sm {\n --pf-c-tabs--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--2xl); } }\n\n@media (min-width: 768px) {\n .pf-c-tabs.pf-m-inset-none-on-md {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--m-vertical--inset: 0;\n --pf-c-tabs--m-vertical--m-box--inset: 0; }\n .pf-c-tabs.pf-m-inset-sm-on-md {\n --pf-c-tabs--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--sm); }\n .pf-c-tabs.pf-m-inset-md-on-md {\n --pf-c-tabs--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--md); }\n .pf-c-tabs.pf-m-inset-lg-on-md {\n --pf-c-tabs--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg); }\n .pf-c-tabs.pf-m-inset-xl-on-md {\n --pf-c-tabs--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl); }\n .pf-c-tabs.pf-m-inset-2xl-on-md {\n --pf-c-tabs--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--2xl); } }\n\n@media (min-width: 992px) {\n .pf-c-tabs.pf-m-inset-none-on-lg {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--m-vertical--inset: 0;\n --pf-c-tabs--m-vertical--m-box--inset: 0; }\n .pf-c-tabs.pf-m-inset-sm-on-lg {\n --pf-c-tabs--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--sm); }\n .pf-c-tabs.pf-m-inset-md-on-lg {\n --pf-c-tabs--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--md); }\n .pf-c-tabs.pf-m-inset-lg-on-lg {\n --pf-c-tabs--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg); }\n .pf-c-tabs.pf-m-inset-xl-on-lg {\n --pf-c-tabs--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl); }\n .pf-c-tabs.pf-m-inset-2xl-on-lg {\n --pf-c-tabs--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--2xl); } }\n\n@media (min-width: 1200px) {\n .pf-c-tabs.pf-m-inset-none-on-xl {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--m-vertical--inset: 0;\n --pf-c-tabs--m-vertical--m-box--inset: 0; }\n .pf-c-tabs.pf-m-inset-sm-on-xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--sm); }\n .pf-c-tabs.pf-m-inset-md-on-xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--md); }\n .pf-c-tabs.pf-m-inset-lg-on-xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg); }\n .pf-c-tabs.pf-m-inset-xl-on-xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl); }\n .pf-c-tabs.pf-m-inset-2xl-on-xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--2xl); } }\n\n@media (min-width: 1450px) {\n .pf-c-tabs.pf-m-inset-none-on-2xl {\n --pf-c-tabs--inset: 0;\n --pf-c-tabs--m-vertical--inset: 0;\n --pf-c-tabs--m-vertical--m-box--inset: 0; }\n .pf-c-tabs.pf-m-inset-sm-on-2xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--sm);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--sm); }\n .pf-c-tabs.pf-m-inset-md-on-2xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--md);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--md); }\n .pf-c-tabs.pf-m-inset-lg-on-2xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--lg);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--lg); }\n .pf-c-tabs.pf-m-inset-xl-on-2xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--xl); }\n .pf-c-tabs.pf-m-inset-2xl-on-2xl {\n --pf-c-tabs--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--inset: var(--pf-global--spacer--2xl);\n --pf-c-tabs--m-vertical--m-box--inset: var(--pf-global--spacer--2xl); } }\n\n.pf-c-tile {\n --pf-c-tile--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-tile--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-tile--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-tile--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-tile--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-tile--before--BorderColor: var(--pf-global--BorderColor--100);\n --pf-c-tile--before--BorderWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-tile--before--BorderRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-tile--hover--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-tile--m-selected--before--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-tile--m-selected--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-tile--focus--before--BorderWidth: var(--pf-global--BorderWidth--md);\n --pf-c-tile--focus--before--BorderColor: var(--pf-global--primary-color--100);\n --pf-c-tile--m-disabled--BackgroundColor: var(--pf-global--disabled-color--300);\n --pf-c-tile__title--Color: var(--pf-global--Color--100);\n --pf-c-tile--hover__title--Color: var(--pf-global--primary-color--100);\n --pf-c-tile--m-selected__title--Color: var(--pf-global--primary-color--100);\n --pf-c-tile--focus__title--Color: var(--pf-global--primary-color--100);\n --pf-c-tile--m-disabled__title--Color: var(--pf-global--disabled-color--100);\n --pf-c-tile__icon--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-tile__icon--FontSize: var(--pf-global--icon--FontSize--md);\n --pf-c-tile__icon--Color: var(--pf-global--Color--100);\n --pf-c-tile--hover__icon--Color: var(--pf-global--primary-color--100);\n --pf-c-tile--m-selected__icon--Color: var(--pf-global--primary-color--100);\n --pf-c-tile--m-disabled__icon--Color: var(--pf-global--disabled-color--100);\n --pf-c-tile--focus__icon--Color: var(--pf-global--primary-color--100);\n --pf-c-tile__header--m-stacked__icon--MarginBottom: var(--pf-global--spacer--xs);\n --pf-c-tile__header--m-stacked__icon--FontSize: var(--pf-global--icon--FontSize--lg);\n --pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize: var(--pf-global--icon--FontSize--xl);\n --pf-c-tile__body--Color: var(--pf-global--Color--100);\n --pf-c-tile__body--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-tile--m-disabled__body--Color: var(--pf-global--disabled-color--100);\n position: relative;\n display: inline-grid;\n padding: var(--pf-c-tile--PaddingTop) var(--pf-c-tile--PaddingRight) var(--pf-c-tile--PaddingBottom) var(--pf-c-tile--PaddingLeft);\n text-align: center;\n cursor: pointer;\n background-color: var(--pf-c-tile--BackgroundColor);\n grid-template-rows: min-content; }\n .pf-c-tile::before {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n content: \"\";\n border: var(--pf-c-tile--before--BorderWidth) solid var(--pf-c-tile--before--BorderColor);\n border-radius: var(--pf-c-tile--before--BorderRadius); }\n .pf-c-tile:hover {\n --pf-c-tile__title--Color: var(--pf-c-tile--hover__title--Color);\n --pf-c-tile__icon--Color: var(--pf-c-tile--hover__icon--Color);\n --pf-c-tile--before--BorderColor: var(--pf-c-tile--hover--before--BorderColor); }\n .pf-c-tile.pf-m-selected {\n --pf-c-tile__title--Color: var(--pf-c-tile--m-selected__title--Color);\n --pf-c-tile__icon--Color: var(--pf-c-tile--m-selected__icon--Color);\n --pf-c-tile--before--BorderWidth: var(--pf-c-tile--m-selected--before--BorderWidth);\n --pf-c-tile--before--BorderColor: var(--pf-c-tile--m-selected--before--BorderColor); }\n .pf-c-tile:focus {\n --pf-c-tile__title--Color: var(--pf-c-tile--focus__title--Color);\n --pf-c-tile__icon--Color: var(--pf-c-tile--focus__icon--Color);\n --pf-c-tile--before--BorderWidth: var(--pf-c-tile--focus--before--BorderWidth);\n --pf-c-tile--before--BorderColor: var(--pf-c-tile--focus--before--BorderColor); }\n .pf-c-tile.pf-m-disabled {\n --pf-c-tile--BackgroundColor: var(--pf-c-tile--m-disabled--BackgroundColor);\n --pf-c-tile__title--Color: var(--pf-c-tile--m-disabled__title--Color);\n --pf-c-tile__body--Color: var(--pf-c-tile--m-disabled__body--Color);\n --pf-c-tile--before--BorderWidth: 0;\n --pf-c-tile__icon--Color: var(--pf-c-tile--m-disabled__icon--Color);\n pointer-events: none; }\n .pf-c-tile.pf-m-display-lg .pf-c-tile__header.pf-m-stacked {\n --pf-c-tile__icon--FontSize: var(--pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize); }\n\n.pf-c-tile__header {\n display: flex;\n align-items: center;\n justify-content: center; }\n .pf-c-tile__header.pf-m-stacked {\n --pf-c-tile__icon--MarginRight: 0;\n --pf-c-tile__icon--FontSize: var(--pf-c-tile__header--m-stacked__icon--FontSize);\n flex-direction: column;\n justify-content: initial; }\n .pf-c-tile__header.pf-m-stacked .pf-c-tile__icon {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: var(--pf-c-tile__header--m-stacked__icon--MarginBottom); }\n\n.pf-c-tile__title {\n color: var(--pf-c-tile__title--Color); }\n\n.pf-c-tile__body {\n font-size: var(--pf-c-tile__body--FontSize);\n color: var(--pf-c-tile__body--Color); }\n\n.pf-c-tile__icon {\n margin-right: var(--pf-c-tile__icon--MarginRight);\n font-size: var(--pf-c-tile__icon--FontSize);\n color: var(--pf-c-tile__icon--Color); }\n\n.pf-c-title {\n --pf-c-title--FontFamily: var(--pf-global--FontFamily--heading--sans-serif);\n --pf-c-title--m-4xl--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-title--m-4xl--FontSize: var(--pf-global--FontSize--4xl);\n --pf-c-title--m-4xl--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-title--m-3xl--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-title--m-3xl--FontSize: var(--pf-global--FontSize--3xl);\n --pf-c-title--m-3xl--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-title--m-2xl--LineHeight: var(--pf-global--LineHeight--sm);\n --pf-c-title--m-2xl--FontSize: var(--pf-global--FontSize--2xl);\n --pf-c-title--m-2xl--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-title--m-xl--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-title--m-xl--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-title--m-xl--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-title--m-lg--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-title--m-lg--FontSize: var(--pf-global--FontSize--lg);\n --pf-c-title--m-lg--FontWeight: var(--pf-global--FontWeight--normal);\n --pf-c-title--m-md--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-title--m-md--FontSize: var(--pf-global--FontSize--md);\n --pf-c-title--m-md--FontWeight: var(--pf-global--FontWeight--normal);\n font-family: var(--pf-c-title--FontFamily);\n word-break: break-word; }\n .pf-c-title.pf-m-4xl {\n font-size: var(--pf-c-title--m-4xl--FontSize);\n font-weight: var(--pf-c-title--m-4xl--FontWeight);\n line-height: var(--pf-c-title--m-4xl--LineHeight); }\n .pf-c-title.pf-m-3xl {\n font-size: var(--pf-c-title--m-3xl--FontSize);\n font-weight: var(--pf-c-title--m-3xl--FontWeight);\n line-height: var(--pf-c-title--m-3xl--LineHeight); }\n .pf-c-title.pf-m-2xl {\n font-size: var(--pf-c-title--m-2xl--FontSize);\n font-weight: var(--pf-c-title--m-2xl--FontWeight);\n line-height: var(--pf-c-title--m-2xl--LineHeight); }\n .pf-c-title.pf-m-xl {\n font-size: var(--pf-c-title--m-xl--FontSize);\n font-weight: var(--pf-c-title--m-xl--FontWeight);\n line-height: var(--pf-c-title--m-xl--LineHeight); }\n .pf-c-title.pf-m-lg {\n font-size: var(--pf-c-title--m-lg--FontSize);\n font-weight: var(--pf-c-title--m-lg--FontWeight);\n line-height: var(--pf-c-title--m-lg--LineHeight); }\n .pf-c-title.pf-m-md {\n font-size: var(--pf-c-title--m-md--FontSize);\n font-weight: var(--pf-c-title--m-md--FontWeight);\n line-height: var(--pf-c-title--m-md--LineHeight); }\n\n.pf-m-overpass-font .pf-c-title {\n --pf-c-title--m-md--FontWeight: var(--pf-global--FontWeight--semi-bold);\n --pf-c-title--m-lg--FontWeight: var(--pf-global--FontWeight--semi-bold); }\n\n.pf-c-toggle-group {\n --pf-c-toggle-group__button--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-toggle-group__button--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-toggle-group__button--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-toggle-group__button--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-toggle-group__button--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-toggle-group__button--LineHeight: calc(var(--pf-global--FontSize--md) * var(--pf-global--LineHeight--md));\n --pf-c-toggle-group__button--Color: var(--pf-global--Color--100);\n --pf-c-toggle-group__button--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-toggle-group__button--hover--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-toggle-group__button--focus--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-toggle-group__button--disabled--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-toggle-group__button--disabled--Color: var(--pf-global--disabled-color--100);\n --pf-c-toggle-group__item--first-child__button--BorderTopLeftRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-toggle-group__item--first-child__button--BorderBottomLeftRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-toggle-group__item--last-child__button--BorderTopRightRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-toggle-group__item--last-child__button--BorderBottomRightRadius: var(--pf-global--BorderRadius--sm);\n --pf-c-toggle-group__icon--text--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-toggle-group__button--m-light--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-toggle-group__button--m-selected--BackgroundColor: var(--pf-global--primary-color--100);\n --pf-c-toggle-group__button--m-selected--Color: var(--pf-global--Color--light-100);\n display: flex; }\n\n.pf-c-toggle-group__item:first-child .pf-c-toggle-group__button {\n border-top-left-radius: var(--pf-c-toggle-group__item--first-child__button--BorderTopLeftRadius);\n border-bottom-left-radius: var(--pf-c-toggle-group__item--first-child__button--BorderBottomLeftRadius); }\n\n.pf-c-toggle-group__item:last-child .pf-c-toggle-group__button {\n border-top-right-radius: var(--pf-c-toggle-group__item--last-child__button--BorderTopRightRadius);\n border-bottom-right-radius: var(--pf-c-toggle-group__item--last-child__button--BorderBottomRightRadius); }\n\n.pf-c-toggle-group__button {\n display: inline-flex;\n padding: var(--pf-c-toggle-group__button--PaddingTop) var(--pf-c-toggle-group__button--PaddingRight) var(--pf-c-toggle-group__button--PaddingBottom) var(--pf-c-toggle-group__button--PaddingLeft);\n font-size: var(--pf-c-toggle-group__button--FontSize);\n line-height: var(--pf-c-toggle-group__button--LineHeight);\n color: var(--pf-c-toggle-group__button--Color);\n background-color: var(--pf-c-toggle-group__button--BackgroundColor);\n border: 0; }\n .pf-c-toggle-group__button.pf-m-light {\n --pf-c-toggle-group__button--BackgroundColor: var(--pf-c-toggle-group__button--m-light--BackgroundColor); }\n .pf-c-toggle-group__button:hover {\n --pf-c-toggle-group__button--BackgroundColor: var(--pf-c-toggle-group__button--hover--BackgroundColor);\n text-decoration: none; }\n .pf-c-toggle-group__button:focus {\n --pf-c-toggle-group__button--BackgroundColor: var(--pf-c-toggle-group__button--focus--BackgroundColor); }\n .pf-c-toggle-group__button.pf-m-selected {\n --pf-c-toggle-group__button--BackgroundColor: var(--pf-c-toggle-group__button--m-selected--BackgroundColor);\n --pf-c-toggle-group__button--Color: var(--pf-c-toggle-group__button--m-selected--Color); }\n .pf-c-toggle-group__button:disabled, .pf-c-toggle-group__button.pf-m-disabled {\n --pf-c-toggle-group__button--BackgroundColor: var(--pf-c-toggle-group__button--disabled--BackgroundColor);\n --pf-c-toggle-group__button--Color: var(--pf-c-toggle-group__button--disabled--Color);\n pointer-events: none; }\n\n.pf-c-toggle-group__icon + .pf-c-toggle-group__text,\n.pf-c-toggle-group__text + .pf-c-toggle-group__icon {\n margin-left: var(--pf-c-toggle-group__icon--text--MarginLeft); }\n\n.pf-c-tooltip {\n --pf-c-tooltip--MaxWidth: 18.75rem;\n --pf-c-tooltip--BoxShadow: var(--pf-global--BoxShadow--md);\n --pf-c-tooltip__content--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-tooltip__content--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-tooltip__content--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-tooltip__content--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-tooltip__content--Color: var(--pf-global--Color--light-100);\n --pf-c-tooltip__content--BackgroundColor: var(--pf-global--BackgroundColor--dark-100);\n --pf-c-tooltip__content--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-tooltip__arrow--Width: var(--pf-global--arrow--width);\n --pf-c-tooltip__arrow--Height: var(--pf-global--arrow--width);\n --pf-c-tooltip__arrow--m-top--TranslateX: -50%;\n --pf-c-tooltip__arrow--m-top--TranslateY: 50%;\n --pf-c-tooltip__arrow--m-top--Rotate: 45deg;\n --pf-c-tooltip__arrow--m-right--TranslateX: -50%;\n --pf-c-tooltip__arrow--m-right--TranslateY: -50%;\n --pf-c-tooltip__arrow--m-right--Rotate: 45deg;\n --pf-c-tooltip__arrow--m-bottom--TranslateX: -50%;\n --pf-c-tooltip__arrow--m-bottom--TranslateY: -50%;\n --pf-c-tooltip__arrow--m-bottom--Rotate: 45deg;\n --pf-c-tooltip__arrow--m-left--TranslateX: 50%;\n --pf-c-tooltip__arrow--m-left--TranslateY: -50%;\n --pf-c-tooltip__arrow--m-left--Rotate: 45deg;\n position: relative;\n max-width: var(--pf-c-tooltip--MaxWidth);\n box-shadow: var(--pf-c-tooltip--BoxShadow); }\n .pf-c-tooltip.pf-m-top .pf-c-tooltip__arrow {\n bottom: 0;\n left: 50%;\n transform: translateX(var(--pf-c-tooltip__arrow--m-top--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-top--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-top--Rotate)); }\n .pf-c-tooltip.pf-m-bottom .pf-c-tooltip__arrow {\n top: 0;\n left: 50%;\n transform: translateX(var(--pf-c-tooltip__arrow--m-bottom--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-bottom--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-bottom--Rotate)); }\n .pf-c-tooltip.pf-m-left .pf-c-tooltip__arrow {\n top: 50%;\n right: 0;\n transform: translateX(var(--pf-c-tooltip__arrow--m-left--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-left--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-left--Rotate)); }\n .pf-c-tooltip.pf-m-right .pf-c-tooltip__arrow {\n top: 50%;\n left: 0;\n transform: translateX(var(--pf-c-tooltip__arrow--m-right--TranslateX)) translateY(var(--pf-c-tooltip__arrow--m-right--TranslateY)) rotate(var(--pf-c-tooltip__arrow--m-right--Rotate)); }\n\n.pf-c-tooltip__content {\n position: relative;\n padding: var(--pf-c-tooltip__content--PaddingTop) var(--pf-c-tooltip__content--PaddingRight) var(--pf-c-tooltip__content--PaddingBottom) var(--pf-c-tooltip__content--PaddingLeft);\n font-size: var(--pf-c-tooltip__content--FontSize);\n color: var(--pf-c-tooltip__content--Color);\n text-align: center;\n word-break: break-word;\n background-color: var(--pf-c-tooltip__content--BackgroundColor); }\n .pf-c-tooltip__content.pf-m-text-align-left {\n text-align: left; }\n\n.pf-c-tooltip__arrow {\n position: absolute;\n width: var(--pf-c-tooltip__arrow--Width);\n height: var(--pf-c-tooltip__arrow--Height);\n pointer-events: none;\n background-color: var(--pf-c-tooltip__content--BackgroundColor); }\n\n.pf-c-touchspin {\n --pf-c-touchspin__unit--c-input-group--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-touchspin__icon--FontSize: var(--pf-global--FontSize--xs);\n --pf-c-touchspin--c-form-control--width-base: calc(var(--pf-global--spacer--sm) * 2);\n --pf-c-touchspin--c-form-control--width-chars: 4;\n --pf-c-touchspin--c-form-control--Width: calc(var(--pf-c-touchspin--c-form-control--width-base) + var(--pf-c-touchspin--c-form-control--width-chars) * 1ch);\n display: inline-flex;\n align-items: center; }\n .pf-c-touchspin .pf-c-form-control {\n display: inline-flex;\n width: var(--pf-c-touchspin--c-form-control--Width);\n text-align: right; }\n\n.pf-c-input-group + .pf-c-touchspin__unit,\n.pf-c-touchspin__unit + .pf-c-input-group {\n margin-left: var(--pf-c-touchspin__unit--c-input-group--MarginLeft); }\n\n.pf-c-touchspin__icon {\n font-size: var(--pf-c-touchspin__icon--FontSize); }\n\n.pf-c-tree-view {\n --pf-c-tree-view--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-tree-view--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node--indent--base: calc(var(--pf-global--spacer--md) * 2 + var(--pf-c-tree-view__node-toggle-icon--MinWidth));\n --pf-c-tree-view__node--nested-indent--base: calc(var(--pf-c-tree-view__node--indent--base) - var(--pf-global--spacer--md));\n --pf-c-tree-view__node--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node--PaddingLeft: 0;\n --pf-c-tree-view__node--Color: var(--pf-global--Color--100);\n --pf-c-tree-view__node--m-current--Color: var(--pf-global--link--Color);\n --pf-c-tree-view__node--m-current--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-tree-view__node--hover--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-tree-view__node--focus--BackgroundColor: var(--pf-global--palette--black-200);\n --pf-c-tree-view__list-item__list-item__node-toggle--Top: var(--pf-c-tree-view__node--PaddingTop);\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft);\n --pf-c-tree-view__list-item__list-item__node-toggle--TranslateX: -100%;\n --pf-c-tree-view__node-toggle-icon--MinWidth: var(--pf-global--FontSize--md);\n --pf-c-tree-view__node-toggle-icon--Transition: var(--pf-global--Transition);\n --pf-c-tree-view__node-toggle-button--PaddingTop: var(--pf-global--spacer--form-element);\n --pf-c-tree-view__node-toggle-button--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-tree-view__node-toggle-button--PaddingBottom: var(--pf-global--spacer--form-element);\n --pf-c-tree-view__node-toggle-button--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-tree-view__node-toggle-button--MarginTop: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-tree-view__node-toggle-button--MarginBottom: calc(var(--pf-global--spacer--form-element) * -1);\n --pf-c-tree-view__node-check--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node-count--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node-count--c-badge--m-read--BackgroundColor: var(--pf-global--disabled-color--200);\n --pf-c-tree-view__search--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-tree-view__search--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-tree-view__search--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-tree-view__search--PaddingLeft: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node-icon--PaddingRight: var(--pf-global--spacer--sm);\n --pf-c-tree-view__node-icon--Color: var(--pf-global--icon--Color--light);\n --pf-c-tree-view__list-item--m-expanded__node-toggle-icon--Rotate: 90deg;\n --pf-c-tree-view__node-text--max-lines: 1;\n --pf-c-tree-view__action--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-tree-view__action--focus--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-tree-view__action--Color: var(--pf-global--icon--Color--light);\n --pf-c-tree-view__action--hover--Color: var(--pf-global--icon--Color--dark);\n --pf-c-tree-view__action--focus--Color: var(--pf-global--icon--Color--dark);\n padding-top: var(--pf-c-tree-view--PaddingTop);\n padding-bottom: var(--pf-c-tree-view--PaddingBottom); }\n\n.pf-c-tree-view__list-item.pf-m-expanded > .pf-c-tree-view__content > .pf-c-tree-view__node > .pf-c-tree-view__node-toggle > .pf-c-tree-view__node-toggle-icon {\n transform: rotate(var(--pf-c-tree-view__list-item--m-expanded__node-toggle-icon--Rotate));\n text-align: center; }\n\n.pf-c-tree-view__node {\n position: relative;\n display: flex;\n flex: 1 1;\n align-items: center;\n min-width: 0;\n padding: var(--pf-c-tree-view__node--PaddingTop) var(--pf-c-tree-view__node--PaddingRight) var(--pf-c-tree-view__node--PaddingBottom) var(--pf-c-tree-view__node--PaddingLeft);\n color: var(--pf-c-tree-view__node--Color);\n text-align: left;\n cursor: pointer;\n border: 0; }\n .pf-c-tree-view__node.pf-m-current {\n --pf-c-tree-view__node--Color: var(--pf-c-tree-view__node--m-current--Color);\n font-weight: var(--pf-c-tree-view__node--m-current--FontWeight); }\n .pf-c-tree-view__node:focus {\n background-color: var(--pf-c-tree-view__node--focus--BackgroundColor); }\n .pf-c-tree-view__node .pf-c-tree-view__node-count {\n margin-left: var(--pf-c-tree-view__node-count--MarginLeft); }\n .pf-c-tree-view__node .pf-c-tree-view__node-count .pf-c-badge.pf-m-read {\n --pf-c-badge--m-read--BackgroundColor: var(--pf-c-tree-view__node-count--c-badge--m-read--BackgroundColor); }\n\n.pf-c-tree-view__node-toggle-icon {\n display: inline-block;\n min-width: var(--pf-c-tree-view__node-toggle-icon--MinWidth);\n transition: var(--pf-c-tree-view__node-toggle-icon--Transition); }\n\n.pf-c-tree-view__node-check {\n margin-right: var(--pf-c-tree-view__node-check--MarginRight); }\n\n.pf-c-tree-view__node-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: var(--pf-c-tree-view__node-toggle-button--PaddingTop) var(--pf-c-tree-view__node-toggle-button--PaddingRight) var(--pf-c-tree-view__node-toggle-button--PaddingBottom) var(--pf-c-tree-view__node-toggle-button--PaddingLeft);\n margin-top: var(--pf-c-tree-view__node-toggle-button--MarginTop);\n margin-bottom: var(--pf-c-tree-view__node-toggle-button--MarginBottom);\n border: 0; }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__node-toggle {\n position: absolute;\n top: var(--pf-c-tree-view__list-item__list-item__node-toggle--Top);\n left: var(--pf-c-tree-view__list-item__list-item__node-toggle--Left);\n transform: translateX(var(--pf-c-tree-view__list-item__list-item__node-toggle--TranslateX)); }\n\n.pf-c-tree-view__node-text {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap; }\n\n.pf-c-tree-view__search {\n padding: var(--pf-c-tree-view__search--PaddingTop) var(--pf-c-tree-view__search--PaddingRight) var(--pf-c-tree-view__search--PaddingBottom) var(--pf-c-tree-view__search--PaddingLeft); }\n\n.pf-c-tree-view__node-icon {\n padding-right: var(--pf-c-tree-view__node-icon--PaddingRight);\n color: var(--pf-c-tree-view__node-icon--Color); }\n\n.pf-c-tree-view__content {\n display: flex;\n align-items: center; }\n\n.pf-c-tree-view__content:hover,\n.pf-c-tree-view__content:focus-within {\n background-color: var(--pf-c-tree-view__node--hover--BackgroundColor); }\n\n.pf-c-tree-view__action {\n margin-left: var(--pf-c-tree-view__action--MarginLeft);\n color: var(--pf-c-tree-view__action--Color);\n border: 0; }\n .pf-c-tree-view__action:hover {\n --pf-c-tree-view__action--Color: var(--pf-c-tree-view__action--hover--Color); }\n .pf-c-tree-view__action:focus {\n --pf-c-tree-view__action--Color: var(--pf-c-tree-view__action--focus--Color);\n background-color: var(--pf-c-tree-view__action--focus--BackgroundColor); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 1 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 2 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 3 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 4 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 5 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 6 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 7 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 8 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 9 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item .pf-c-tree-view__list-item {\n --pf-c-tree-view__node--PaddingLeft: calc(var(--pf-c-tree-view__node--nested-indent--base) * 10 + var(--pf-c-tree-view__node--indent--base));\n --pf-c-tree-view__list-item__list-item__node-toggle--Left: var(--pf-c-tree-view__node--PaddingLeft); }\n\n.pf-c-wizard {\n --pf-c-wizard--Height: 100%;\n --pf-c-modal-box--c-wizard--FlexBasis: 47.625rem;\n --pf-c-wizard__header--BackgroundColor: var(--pf-global--BackgroundColor--dark-100);\n --pf-c-wizard__header--ZIndex: var(--pf-global--ZIndex--md);\n --pf-c-wizard__header--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-wizard__header--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__header--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-wizard__header--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-wizard__header--lg--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__header--lg--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-wizard__header--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-wizard__header--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-wizard__close--Top: calc(var(--pf-global--spacer--lg) - var(--pf-global--spacer--form-element));\n --pf-c-wizard__close--Right: 0;\n --pf-c-wizard__close--xl--Right: var(--pf-global--spacer--lg);\n --pf-c-wizard__close--FontSize: var(--pf-global--FontSize--xl);\n --pf-c-wizard__title--PaddingRight: var(--pf-global--spacer--2xl);\n --pf-c-wizard__description--PaddingTop: var(--pf-global--spacer--sm);\n --pf-c-wizard__description--Color: var(--pf-global--Color--light-200);\n --pf-c-wizard__nav-link--Color: var(--pf-global--Color--100);\n --pf-c-wizard__nav-link--TextDecoration: var(--pf-global--link--TextDecoration);\n --pf-c-wizard__nav-link--hover--Color: var(--pf-global--link--Color);\n --pf-c-wizard__nav-link--focus--Color: var(--pf-global--link--Color);\n --pf-c-wizard__nav-link--m-current--Color: var(--pf-global--link--Color);\n --pf-c-wizard__nav-link--m-current--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-wizard__nav-link--m-disabled--Color: var(--pf-global--Color--dark-200);\n --pf-c-wizard__nav-list__nav-list__nav-link--m-current--FontWeight: var(--pf-global--FontWeight--bold);\n --pf-c-wizard__nav-link--before--Width: 1.5rem;\n --pf-c-wizard__nav-link--before--Height: 1.5rem;\n --pf-c-wizard__nav-link--before--Top: 0;\n --pf-c-wizard__nav-link--before--BackgroundColor: var(--pf-global--BackgroundColor--200);\n --pf-c-wizard__nav-link--before--BorderRadius: var(--pf-global--BorderRadius--lg);\n --pf-c-wizard__nav-link--before--Color: var(--pf-global--Color--100);\n --pf-c-wizard__nav-link--before--FontSize: var(--pf-global--FontSize--sm);\n --pf-c-wizard__nav-link--before--TranslateX: calc(-100% - var(--pf-global--spacer--sm));\n --pf-c-wizard__nav-link--m-current--before--BackgroundColor: var(--pf-global--active-color--100);\n --pf-c-wizard__nav-link--m-current--before--Color: var(--pf-global--Color--light-100);\n --pf-c-wizard__nav-link--m-disabled--before--BackgroundColor: transparent;\n --pf-c-wizard__nav-link--m-disabled--before--Color: var(--pf-global--Color--dark-200);\n --pf-c-wizard__toggle--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-wizard__toggle--ZIndex: var(--pf-global--ZIndex--md);\n --pf-c-wizard__toggle--BoxShadow: var(--pf-global--BoxShadow--md-bottom);\n --pf-c-wizard__toggle--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-wizard__toggle--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__toggle--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-wizard__toggle--PaddingLeft: calc(var(--pf-global--spacer--md) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));\n --pf-c-wizard__toggle--m-expanded--BorderBottomWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-wizard__toggle--m-expanded--BorderBottomColor: var(--pf-global--BorderColor--100);\n --pf-c-wizard--m-in-page__toggle--xl--PaddingLeft: calc(var(--pf-global--spacer--xl) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));\n --pf-c-wizard__toggle-num--before--Top: 0;\n --pf-c-wizard__toggle-list-item--not-last-child--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-wizard__toggle-list-item--MarginBottom: var(--pf-global--spacer--xs);\n --pf-c-wizard__toggle-list--MarginRight: var(--pf-global--spacer--sm);\n --pf-c-wizard__toggle-list--MarginBottom: calc(var(--pf-c-wizard__toggle-list-item--MarginBottom) * -1);\n --pf-c-wizard__toggle-separator--MarginLeft: var(--pf-global--spacer--sm);\n --pf-c-wizard__toggle-separator--Color: var(--pf-global--BorderColor--200);\n --pf-c-wizard__toggle-icon--LineHeight: var(--pf-global--LineHeight--md);\n --pf-c-wizard__toggle--m-expanded__toggle-icon--Rotate: 180deg;\n --pf-c-wizard__nav--ZIndex: var(--pf-global--ZIndex--sm);\n --pf-c-wizard__nav--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-wizard__nav--BoxShadow: var(--pf-global--BoxShadow--md-bottom);\n --pf-c-wizard__nav--Width: 100%;\n --pf-c-wizard__nav--lg--Width: 15.625rem;\n --pf-c-wizard__nav--lg--BorderRightWidth: var(--pf-global--BorderWidth--sm);\n --pf-c-wizard__nav--lg--BorderRightColor: var(--pf-global--BorderColor--100);\n --pf-c-wizard__nav-list--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-wizard__nav-list--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__nav-list--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-wizard__nav-list--PaddingLeft: calc(var(--pf-global--spacer--md) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));\n --pf-c-wizard__nav-list--lg--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-wizard__nav-list--lg--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__nav-list--lg--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-wizard__nav-list--xl--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-wizard__nav-list--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-wizard__nav-list--xl--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-wizard__nav-list--xl--PaddingLeft: calc(var(--pf-global--spacer--lg) + var(--pf-c-wizard__nav-link--before--Width) + var(--pf-global--spacer--sm));\n --pf-c-wizard__nav-list--nested--MarginLeft: var(--pf-global--spacer--md);\n --pf-c-wizard__nav-list--nested--MarginTop: var(--pf-global--spacer--md);\n --pf-c-wizard__nav-item--MarginTop: var(--pf-global--spacer--md);\n --pf-c-wizard__outer-wrap--BackgroundColor: var(--pf-global--BackgroundColor--100);\n --pf-c-wizard__outer-wrap--lg--PaddingLeft: var(--pf-c-wizard__nav--Width);\n --pf-c-wizard__main--ZIndex: var(--pf-global--ZIndex--xs);\n --pf-c-wizard__main-body--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-wizard__main-body--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__main-body--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-wizard__main-body--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-wizard__main-body--xl--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-wizard__main-body--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-wizard__main-body--xl--PaddingBottom: var(--pf-global--spacer--lg);\n --pf-c-wizard__main-body--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-wizard__footer--PaddingTop: var(--pf-global--spacer--md);\n --pf-c-wizard__footer--PaddingRight: var(--pf-global--spacer--md);\n --pf-c-wizard__footer--PaddingBottom: var(--pf-global--spacer--sm);\n --pf-c-wizard__footer--PaddingLeft: var(--pf-global--spacer--md);\n --pf-c-wizard__footer--xl--PaddingTop: var(--pf-global--spacer--lg);\n --pf-c-wizard__footer--xl--PaddingRight: var(--pf-global--spacer--lg);\n --pf-c-wizard__footer--xl--PaddingBottom: var(--pf-global--spacer--md);\n --pf-c-wizard__footer--xl--PaddingLeft: var(--pf-global--spacer--lg);\n --pf-c-wizard__footer--child--MarginRight: var(--pf-global--spacer--md);\n --pf-c-wizard__footer--child--MarginBottom: var(--pf-global--spacer--sm);\n position: relative;\n display: flex;\n flex-direction: column;\n height: var(--pf-c-wizard--Height); }\n @media screen and (min-width: 992px) {\n .pf-c-wizard {\n --pf-c-wizard__header--PaddingRight: var(--pf-c-wizard__header--lg--PaddingRight);\n --pf-c-wizard__header--PaddingLeft: var(--pf-c-wizard__header--lg--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-wizard {\n --pf-c-wizard__header--PaddingRight: var(--pf-c-wizard__header--xl--PaddingRight);\n --pf-c-wizard__header--PaddingLeft: var(--pf-c-wizard__header--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-wizard {\n --pf-c-wizard__close--Right: var(--pf-c-wizard__close--xl--Right); } }\n @media screen and (min-width: 992px) {\n .pf-c-wizard {\n --pf-c-wizard__nav--Width: var(--pf-c-wizard__nav--lg--Width);\n --pf-c-wizard__nav--BoxShadow: none; } }\n @media screen and (min-width: 992px) {\n .pf-c-wizard {\n --pf-c-wizard__nav-list--PaddingTop: var(--pf-c-wizard__nav-list--lg--PaddingTop);\n --pf-c-wizard__nav-list--PaddingRight: var(--pf-c-wizard__nav-list--lg--PaddingRight);\n --pf-c-wizard__nav-list--PaddingBottom: var(--pf-c-wizard__nav-list--lg--PaddingBottom); } }\n @media screen and (min-width: 1200px) {\n .pf-c-wizard {\n --pf-c-wizard__nav-list--PaddingTop: var(--pf-c-wizard__nav-list--xl--PaddingTop);\n --pf-c-wizard__nav-list--PaddingRight: var(--pf-c-wizard__nav-list--xl--PaddingRight);\n --pf-c-wizard__nav-list--PaddingBottom: var(--pf-c-wizard__nav-list--xl--PaddingBottom);\n --pf-c-wizard__nav-list--PaddingLeft: var(--pf-c-wizard__nav-list--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-wizard {\n --pf-c-wizard__main-body--PaddingTop: var(--pf-c-wizard__main-body--xl--PaddingTop);\n --pf-c-wizard__main-body--PaddingRight: var(--pf-c-wizard__main-body--xl--PaddingRight);\n --pf-c-wizard__main-body--PaddingBottom: var(--pf-c-wizard__main-body--xl--PaddingBottom);\n --pf-c-wizard__main-body--PaddingLeft: var(--pf-c-wizard__main-body--xl--PaddingLeft); } }\n @media screen and (min-width: 1200px) {\n .pf-c-wizard {\n --pf-c-wizard__footer--PaddingTop: var(--pf-c-wizard__footer--xl--PaddingTop);\n --pf-c-wizard__footer--PaddingRight: var(--pf-c-wizard__footer--xl--PaddingRight);\n --pf-c-wizard__footer--PaddingBottom: var(--pf-c-wizard__footer--xl--PaddingBottom);\n --pf-c-wizard__footer--PaddingLeft: var(--pf-c-wizard__footer--xl--PaddingLeft); } }\n .pf-c-modal-box .pf-c-wizard {\n flex: 1 1 var(--pf-c-modal-box--c-wizard--FlexBasis);\n min-height: 0; }\n .pf-c-wizard > *:not(.pf-c-wizard__outer-wrap) {\n flex-shrink: 0; }\n .pf-c-wizard.pf-m-finished {\n --pf-c-wizard__outer-wrap--lg--PaddingLeft: 0; }\n .pf-c-wizard.pf-m-finished .pf-c-wizard__nav,\n .pf-c-wizard.pf-m-finished .pf-c-wizard__footer,\n .pf-c-wizard.pf-m-finished .pf-c-wizard__toggle {\n display: none;\n visibility: hidden; }\n\n.pf-c-wizard__header {\n color: var(--pf-global--Color--100);\n position: relative;\n z-index: var(--pf-c-wizard__header--ZIndex);\n padding: var(--pf-c-wizard__header--PaddingTop) var(--pf-c-wizard__header--PaddingRight) var(--pf-c-wizard__header--PaddingBottom) var(--pf-c-wizard__header--PaddingLeft);\n background-color: var(--pf-c-wizard__header--BackgroundColor); }\n .pf-c-wizard__header .pf-c-wizard__close {\n position: absolute;\n top: var(--pf-c-wizard__close--Top);\n right: var(--pf-c-wizard__close--Right);\n font-size: var(--pf-c-wizard__close--FontSize); }\n\n.pf-c-wizard__title {\n padding-right: var(--pf-c-wizard__title--PaddingRight);\n word-wrap: break-word; }\n\n.pf-c-wizard__description {\n display: none;\n padding-top: var(--pf-c-wizard__description--PaddingTop);\n color: var(--pf-c-wizard__description--Color);\n visibility: hidden; }\n @media screen and (min-width: 992px) {\n .pf-c-wizard__description {\n display: block;\n visibility: visible; } }\n\n.pf-c-wizard__toggle {\n position: relative;\n z-index: var(--pf-c-wizard__toggle--ZIndex);\n display: flex;\n justify-content: space-between;\n width: 100%;\n padding: var(--pf-c-wizard__toggle--PaddingTop) var(--pf-c-wizard__toggle--PaddingRight) var(--pf-c-wizard__toggle--PaddingBottom) var(--pf-c-wizard__toggle--PaddingLeft);\n background-color: var(--pf-c-wizard__toggle--BackgroundColor);\n border: 0;\n box-shadow: var(--pf-c-wizard__toggle--BoxShadow); }\n @media screen and (min-width: 992px) {\n .pf-c-wizard__toggle {\n display: none;\n visibility: hidden; } }\n .pf-c-wizard__toggle.pf-m-expanded {\n --pf-c-wizard__toggle--BoxShadow: none;\n border-bottom: var(--pf-c-wizard__toggle--m-expanded--BorderBottomWidth) solid var(--pf-c-wizard__toggle--m-expanded--BorderBottomColor); }\n .pf-c-wizard__toggle.pf-m-expanded .pf-c-wizard__toggle-icon {\n transform: rotate(var(--pf-c-wizard__toggle--m-expanded__toggle-icon--Rotate)); }\n\n.pf-c-wizard__toggle-list {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n margin-right: var(--pf-c-wizard__toggle-list--MarginRight);\n margin-bottom: var(--pf-c-wizard__toggle-list--MarginBottom);\n list-style: none; }\n\n.pf-c-wizard__toggle-list-item {\n margin-bottom: var(--pf-c-wizard__toggle-list-item--MarginBottom);\n text-align: left;\n word-break: break-word; }\n .pf-c-wizard__toggle-list-item:not(:last-child) {\n margin-right: var(--pf-c-wizard__toggle-list-item--not-last-child--MarginRight); }\n\n.pf-c-wizard__toggle-num {\n --pf-c-wizard__nav-link--before--Top: var(--pf-c-wizard__toggle-num--before--Top); }\n\n.pf-c-wizard__toggle-separator {\n margin-left: var(--pf-c-wizard__toggle-separator--MarginLeft);\n color: var(--pf-c-wizard__toggle-separator--Color); }\n\n.pf-c-wizard__toggle-icon {\n line-height: var(--pf-c-wizard__toggle-icon--LineHeight); }\n\n.pf-c-wizard__outer-wrap {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n min-height: 0;\n background-color: var(--pf-c-wizard__outer-wrap--BackgroundColor); }\n @media screen and (min-width: 992px) {\n .pf-c-wizard__outer-wrap {\n padding-left: var(--pf-c-wizard__outer-wrap--lg--PaddingLeft); } }\n\n.pf-c-wizard__inner-wrap {\n position: relative;\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n min-height: 0; }\n @media screen and (min-width: 992px) {\n .pf-c-wizard__inner-wrap {\n position: static; } }\n\n.pf-c-wizard__nav {\n position: absolute;\n top: 0;\n left: 0;\n z-index: var(--pf-c-wizard__nav--ZIndex);\n display: none;\n width: var(--pf-c-wizard__nav--Width);\n max-height: 100%;\n overflow-y: auto;\n -webkit-overflow-scrolling: touch;\n visibility: hidden;\n background-color: var(--pf-c-wizard__nav--BackgroundColor);\n box-shadow: var(--pf-c-wizard__nav--BoxShadow); }\n .pf-c-wizard__nav.pf-m-expanded {\n display: block;\n visibility: visible; }\n @media screen and (min-width: 992px) {\n .pf-c-wizard__nav {\n display: block;\n height: 100%;\n visibility: visible;\n border-right: var(--pf-c-wizard__nav--lg--BorderRightWidth) solid var(--pf-c-wizard__nav--lg--BorderRightColor); } }\n\n.pf-c-wizard__nav-list {\n padding-top: var(--pf-c-wizard__nav-list--PaddingTop);\n padding-right: var(--pf-c-wizard__nav-list--PaddingRight);\n padding-bottom: var(--pf-c-wizard__nav-list--PaddingBottom);\n padding-left: var(--pf-c-wizard__nav-list--PaddingLeft);\n list-style: none;\n counter-reset: wizard-nav-count; }\n .pf-c-wizard__nav-list .pf-c-wizard__nav-list {\n padding: 0;\n margin-top: var(--pf-c-wizard__nav-list--nested--MarginTop);\n margin-left: var(--pf-c-wizard__nav-list--nested--MarginLeft); }\n .pf-c-wizard__nav-list .pf-c-wizard__nav-list .pf-c-wizard__nav-link::before {\n content: none; }\n .pf-c-wizard__nav-list .pf-c-wizard__nav-list .pf-c-wizard__nav-link.pf-m-current {\n font-weight: var(--pf-c-wizard__nav-list__nav-list__nav-link--m-current--FontWeight); }\n\n.pf-c-wizard__nav-item + .pf-c-wizard__nav-item {\n margin-top: var(--pf-c-wizard__nav-item--MarginTop); }\n\n.pf-c-wizard__nav-link {\n position: relative;\n display: inline-block;\n color: var(--pf-c-wizard__nav-link--Color);\n text-align: left;\n text-decoration: var(--pf-c-wizard__nav-link--TextDecoration);\n word-break: break-word;\n border: 0; }\n .pf-c-wizard__toggle-num, .pf-c-wizard__nav-link::before {\n position: absolute;\n top: var(--pf-c-wizard__nav-link--before--Top);\n left: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--pf-c-wizard__nav-link--before--Width);\n height: var(--pf-c-wizard__nav-link--before--Height);\n font-size: var(--pf-c-wizard__nav-link--before--FontSize);\n line-height: 1;\n color: var(--pf-c-wizard__nav-link--before--Color);\n background-color: var(--pf-c-wizard__nav-link--before--BackgroundColor);\n border-radius: var(--pf-c-wizard__nav-link--before--BorderRadius);\n transform: translateX(var(--pf-c-wizard__nav-link--before--TranslateX)); }\n .pf-c-wizard__nav-link::before {\n top: 0;\n content: counter(wizard-nav-count);\n counter-increment: wizard-nav-count; }\n .pf-c-wizard__nav-link:hover {\n --pf-c-wizard__nav-link--Color: var(--pf-c-wizard__nav-link--hover--Color); }\n .pf-c-wizard__nav-link:focus {\n --pf-c-wizard__nav-link--Color: var(--pf-c-wizard__nav-link--focus--Color); }\n .pf-c-wizard__nav-link.pf-m-current {\n --pf-c-wizard__nav-link--Color: var(--pf-c-wizard__nav-link--m-current--Color);\n font-weight: var(--pf-c-wizard__nav-link--m-current--FontWeight); }\n .pf-c-wizard__toggle-num, .pf-c-wizard__nav-link.pf-m-current::before {\n --pf-c-wizard__nav-link--before--BackgroundColor: var(--pf-c-wizard__nav-link--m-current--before--BackgroundColor);\n --pf-c-wizard__nav-link--before--Color: var(--pf-c-wizard__nav-link--m-current--before--Color); }\n .pf-c-wizard__nav-link:disabled, .pf-c-wizard__nav-link.pf-m-disabled {\n --pf-c-wizard__nav-link--Color: var(--pf-c-wizard__nav-link--m-disabled--Color);\n pointer-events: none; }\n .pf-c-wizard__nav-link:disabled::before, .pf-c-wizard__nav-link.pf-m-disabled::before {\n --pf-c-wizard__nav-link--before--BackgroundColor: var(--pf-c-wizard__nav-link--m-disabled--before--BackgroundColor);\n --pf-c-wizard__nav-link--before--Color: var(--pf-c-wizard__nav-link--m-disabled--before--Color); }\n\n.pf-c-wizard__main {\n z-index: var(--pf-c-wizard__main--ZIndex);\n flex: 1 1 auto;\n overflow-x: hidden;\n overflow-y: auto;\n word-break: break-word; }\n\n.pf-c-wizard__main-body {\n padding: var(--pf-c-wizard__main-body--PaddingTop) var(--pf-c-wizard__main-body--PaddingRight) var(--pf-c-wizard__main-body--PaddingBottom) var(--pf-c-wizard__main-body--PaddingLeft); }\n .pf-c-wizard__main-body.pf-m-no-padding {\n padding: 0; }\n\n.pf-c-wizard__footer {\n display: flex;\n flex-wrap: wrap;\n flex-shrink: 0;\n padding: var(--pf-c-wizard__footer--PaddingTop) var(--pf-c-wizard__footer--PaddingRight) var(--pf-c-wizard__footer--PaddingBottom) var(--pf-c-wizard__footer--PaddingLeft); }\n .pf-c-wizard__footer > * {\n margin-bottom: var(--pf-c-wizard__footer--child--MarginBottom); }\n .pf-c-wizard__footer > *:not(:last-child) {\n margin-right: var(--pf-c-wizard__footer--child--MarginRight); }\n\n.pf-l-bullseye {\n --pf-l-bullseye--Padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: var(--pf-l-bullseye--Padding);\n margin: 0; }\n\n.pf-l-flex {\n --pf-l-flex--Display: flex;\n --pf-l-flex--FlexWrap: wrap;\n --pf-l-flex--AlignItems: baseline;\n --pf-l-flex--m-row--AlignItems: baseline;\n --pf-l-flex--m-row-reverse--AlignItems: baseline;\n --pf-l-flex--item--Order: 0;\n --pf-l-flex--spacer-base: var(--pf-global--spacer--md);\n --pf-l-flex--spacer: var(--pf-l-flex--spacer-base);\n --pf-l-flex--spacer--none: 0;\n --pf-l-flex--spacer--xs: var(--pf-global--spacer--xs);\n --pf-l-flex--spacer--sm: var(--pf-global--spacer--sm);\n --pf-l-flex--spacer--md: var(--pf-global--spacer--md);\n --pf-l-flex--spacer--lg: var(--pf-global--spacer--lg);\n --pf-l-flex--spacer--xl: var(--pf-global--spacer--xl);\n --pf-l-flex--spacer--2xl: var(--pf-global--spacer--2xl);\n --pf-l-flex--spacer--3xl: var(--pf-global--spacer--3xl);\n --pf-l-flex--spacer--4xl: var(--pf-global--spacer--4xl);\n display: var(--pf-l-flex--Display);\n flex-wrap: var(--pf-l-flex--FlexWrap);\n align-items: var(--pf-l-flex--AlignItems); }\n .pf-l-flex:last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer-base);\n order: var(--pf-l-flex--item--Order);\n max-width: 100%;\n margin-right: var(--pf-l-flex--spacer); }\n @media screen and (min-width: 576px) {\n .pf-l-flex > * {\n order: var(--pf-l-flex--item--Order-on-sm, var(--pf-l-flex--item--Order)); } }\n @media screen and (min-width: 768px) {\n .pf-l-flex > * {\n order: var(--pf-l-flex--item--Order-on-md, var(--pf-l-flex--item--Order-on-sm, var(--pf-l-flex--item--Order))); } }\n @media screen and (min-width: 992px) {\n .pf-l-flex > * {\n order: var(--pf-l-flex--item--Order-on-lg, var(--pf-l-flex--item--Order-on-md, var(--pf-l-flex--item--Order-on-sm, var(--pf-l-flex--item--Order)))); } }\n @media screen and (min-width: 1200px) {\n .pf-l-flex > * {\n order: var(--pf-l-flex--item--Order-on-xl, var(--pf-l-flex--item--Order-on-lg, var(--pf-l-flex--item--Order-on-md, var(--pf-l-flex--item--Order-on-sm, var(--pf-l-flex--item--Order))))); } }\n @media screen and (min-width: 1450px) {\n .pf-l-flex > * {\n order: var(--pf-l-flex--item--Order-on-2xl, var(--pf-l-flex--item--Order-on-xl, var(--pf-l-flex--item--Order-on-lg, var(--pf-l-flex--item--Order-on-md, var(--pf-l-flex--item--Order-on-sm, var(--pf-l-flex--item--Order)))))); } }\n .pf-l-flex > *:last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-flex {\n display: var(--pf-l-flex--Display); }\n\n.pf-l-flex.pf-m-inline-flex {\n --pf-l-flex--Display: inline-flex; }\n\n.pf-l-flex.pf-m-column {\n flex-direction: column;\n align-items: normal; }\n .pf-l-flex.pf-m-column > * {\n margin: 0 0 var(--pf-l-flex--spacer) 0; }\n\n.pf-l-flex.pf-m-column-reverse {\n flex-direction: column-reverse;\n align-items: normal; }\n .pf-l-flex.pf-m-column-reverse > * {\n margin: var(--pf-l-flex--spacer) 0 0 0; }\n\n.pf-l-flex.pf-m-row {\n flex-direction: row;\n align-items: var(--pf-l-flex--m-row--AlignItems); }\n .pf-l-flex.pf-m-row > * {\n margin: 0 var(--pf-l-flex--spacer) 0 0; }\n\n.pf-l-flex.pf-m-row-reverse {\n flex-direction: row-reverse;\n align-items: var(--pf-l-flex--m-row-reverse--AlignItems); }\n .pf-l-flex.pf-m-row-reverse > * {\n margin: 0 0 0 var(--pf-l-flex--spacer); }\n\n.pf-l-flex.pf-m-wrap {\n flex-wrap: wrap; }\n\n.pf-l-flex.pf-m-wrap-reverse {\n flex-wrap: wrap-reverse; }\n\n.pf-l-flex.pf-m-nowrap {\n flex-wrap: nowrap; }\n\n.pf-l-flex.pf-m-justify-content-flex-start {\n justify-content: flex-start; }\n\n.pf-l-flex.pf-m-justify-content-flex-end {\n justify-content: flex-end; }\n\n.pf-l-flex.pf-m-justify-content-center {\n justify-content: center; }\n\n.pf-l-flex.pf-m-justify-content-space-between {\n justify-content: space-between; }\n\n.pf-l-flex.pf-m-justify-content-space-around {\n justify-content: space-around; }\n\n.pf-l-flex.pf-m-justify-content-space-evenly {\n justify-content: space-evenly; }\n\n.pf-l-flex.pf-m-align-items-flex-start {\n align-items: flex-start; }\n\n.pf-l-flex.pf-m-align-items-flex-end {\n align-items: flex-end; }\n\n.pf-l-flex.pf-m-align-items-center {\n align-items: center; }\n\n.pf-l-flex.pf-m-align-items-stretch {\n align-items: stretch; }\n\n.pf-l-flex.pf-m-align-items-baseline {\n align-items: baseline; }\n\n.pf-l-flex.pf-m-align-content-flex-start {\n align-content: flex-start; }\n\n.pf-l-flex.pf-m-align-content-flex-end {\n align-content: flex-end; }\n\n.pf-l-flex.pf-m-align-content-center {\n align-content: center; }\n\n.pf-l-flex.pf-m-align-content-stretch {\n align-content: stretch; }\n\n.pf-l-flex.pf-m-align-content-space-between {\n align-content: space-between; }\n\n.pf-l-flex.pf-m-align-content-space-around {\n align-content: space-around; }\n\n.pf-l-flex > .pf-m-align-right {\n margin-left: auto; }\n\n.pf-l-flex > .pf-m-align-left {\n margin-left: 0; }\n\n.pf-l-flex > .pf-m-grow {\n flex-grow: 1; }\n\n.pf-l-flex > .pf-m-shrink {\n flex-shrink: 1; }\n\n.pf-l-flex > .pf-m-full-width {\n width: 100%;\n margin-right: 0; }\n\n.pf-l-flex > .pf-m-flex-1 {\n flex: 1 0 0; }\n\n.pf-l-flex > .pf-m-flex-2 {\n flex: 2 0 0; }\n\n.pf-l-flex > .pf-m-flex-3 {\n flex: 3 0 0; }\n\n.pf-l-flex > .pf-m-flex-4 {\n flex: 4 0 0; }\n\n.pf-l-flex > .pf-m-flex-default {\n flex: 0 1 auto; }\n\n.pf-l-flex > .pf-m-flex-none {\n flex: none; }\n\n.pf-l-flex > .pf-m-align-self-flex-start {\n align-self: flex-start; }\n\n.pf-l-flex > .pf-m-align-self-flex-end {\n align-self: flex-end; }\n\n.pf-l-flex > .pf-m-align-self-center {\n align-self: center; }\n\n.pf-l-flex > .pf-m-align-self-baseline {\n align-self: baseline; }\n\n.pf-l-flex > .pf-m-align-self-stretch {\n align-self: stretch; }\n\n@media (min-width: 576px) {\n .pf-l-flex.pf-m-flex-on-sm {\n display: var(--pf-l-flex--Display); }\n .pf-l-flex.pf-m-inline-flex-on-sm {\n --pf-l-flex--Display: inline-flex; }\n .pf-l-flex.pf-m-column-on-sm {\n flex-direction: column;\n align-items: normal; }\n .pf-l-flex.pf-m-column-on-sm > * {\n margin: 0 0 var(--pf-l-flex--spacer) 0; }\n .pf-l-flex.pf-m-column-reverse-on-sm {\n flex-direction: column-reverse;\n align-items: normal; }\n .pf-l-flex.pf-m-column-reverse-on-sm > * {\n margin: var(--pf-l-flex--spacer) 0 0 0; }\n .pf-l-flex.pf-m-row-on-sm {\n flex-direction: row;\n align-items: var(--pf-l-flex--m-row--AlignItems); }\n .pf-l-flex.pf-m-row-on-sm > * {\n margin: 0 var(--pf-l-flex--spacer) 0 0; }\n .pf-l-flex.pf-m-row-reverse-on-sm {\n flex-direction: row-reverse;\n align-items: var(--pf-l-flex--m-row-reverse--AlignItems); }\n .pf-l-flex.pf-m-row-reverse-on-sm > * {\n margin: 0 0 0 var(--pf-l-flex--spacer); }\n .pf-l-flex.pf-m-wrap-on-sm {\n flex-wrap: wrap; }\n .pf-l-flex.pf-m-wrap-reverse-on-sm {\n flex-wrap: wrap-reverse; }\n .pf-l-flex.pf-m-nowrap-on-sm {\n flex-wrap: nowrap; }\n .pf-l-flex.pf-m-justify-content-flex-start-on-sm {\n justify-content: flex-start; }\n .pf-l-flex.pf-m-justify-content-flex-end-on-sm {\n justify-content: flex-end; }\n .pf-l-flex.pf-m-justify-content-center-on-sm {\n justify-content: center; }\n .pf-l-flex.pf-m-justify-content-space-between-on-sm {\n justify-content: space-between; }\n .pf-l-flex.pf-m-justify-content-space-around-on-sm {\n justify-content: space-around; }\n .pf-l-flex.pf-m-justify-content-space-evenly-on-sm {\n justify-content: space-evenly; }\n .pf-l-flex.pf-m-align-items-flex-start-on-sm {\n align-items: flex-start; }\n .pf-l-flex.pf-m-align-items-flex-end-on-sm {\n align-items: flex-end; }\n .pf-l-flex.pf-m-align-items-center-on-sm {\n align-items: center; }\n .pf-l-flex.pf-m-align-items-stretch-on-sm {\n align-items: stretch; }\n .pf-l-flex.pf-m-align-items-baseline-on-sm {\n align-items: baseline; }\n .pf-l-flex.pf-m-align-content-flex-start-on-sm {\n align-content: flex-start; }\n .pf-l-flex.pf-m-align-content-flex-end-on-sm {\n align-content: flex-end; }\n .pf-l-flex.pf-m-align-content-center-on-sm {\n align-content: center; }\n .pf-l-flex.pf-m-align-content-stretch-on-sm {\n align-content: stretch; }\n .pf-l-flex.pf-m-align-content-space-between-on-sm {\n align-content: space-between; }\n .pf-l-flex.pf-m-align-content-space-around-on-sm {\n align-content: space-around; }\n .pf-l-flex > .pf-m-align-right-on-sm {\n margin-left: auto; }\n .pf-l-flex > .pf-m-align-left-on-sm {\n margin-left: 0; }\n .pf-l-flex > .pf-m-grow-on-sm {\n flex-grow: 1; }\n .pf-l-flex > .pf-m-shrink-on-sm {\n flex-shrink: 1; }\n .pf-l-flex > .pf-m-full-width-on-sm {\n width: 100%;\n margin-right: 0; }\n .pf-l-flex > .pf-m-flex-1-on-sm {\n flex: 1 0 0; }\n .pf-l-flex > .pf-m-flex-2-on-sm {\n flex: 2 0 0; }\n .pf-l-flex > .pf-m-flex-3-on-sm {\n flex: 3 0 0; }\n .pf-l-flex > .pf-m-flex-4-on-sm {\n flex: 4 0 0; }\n .pf-l-flex > .pf-m-flex-default-on-sm {\n flex: 0 1 auto; }\n .pf-l-flex > .pf-m-flex-none-on-sm {\n flex: none; }\n .pf-l-flex > .pf-m-align-self-flex-start-on-sm {\n align-self: flex-start; }\n .pf-l-flex > .pf-m-align-self-flex-end-on-sm {\n align-self: flex-end; }\n .pf-l-flex > .pf-m-align-self-center-on-sm {\n align-self: center; }\n .pf-l-flex > .pf-m-align-self-baseline-on-sm {\n align-self: baseline; }\n .pf-l-flex > .pf-m-align-self-stretch-on-sm {\n align-self: stretch; } }\n\n@media (min-width: 768px) {\n .pf-l-flex.pf-m-flex-on-md {\n display: var(--pf-l-flex--Display); }\n .pf-l-flex.pf-m-inline-flex-on-md {\n --pf-l-flex--Display: inline-flex; }\n .pf-l-flex.pf-m-column-on-md {\n flex-direction: column;\n align-items: normal; }\n .pf-l-flex.pf-m-column-on-md > * {\n margin: 0 0 var(--pf-l-flex--spacer) 0; }\n .pf-l-flex.pf-m-column-reverse-on-md {\n flex-direction: column-reverse;\n align-items: normal; }\n .pf-l-flex.pf-m-column-reverse-on-md > * {\n margin: var(--pf-l-flex--spacer) 0 0 0; }\n .pf-l-flex.pf-m-row-on-md {\n flex-direction: row;\n align-items: var(--pf-l-flex--m-row--AlignItems); }\n .pf-l-flex.pf-m-row-on-md > * {\n margin: 0 var(--pf-l-flex--spacer) 0 0; }\n .pf-l-flex.pf-m-row-reverse-on-md {\n flex-direction: row-reverse;\n align-items: var(--pf-l-flex--m-row-reverse--AlignItems); }\n .pf-l-flex.pf-m-row-reverse-on-md > * {\n margin: 0 0 0 var(--pf-l-flex--spacer); }\n .pf-l-flex.pf-m-wrap-on-md {\n flex-wrap: wrap; }\n .pf-l-flex.pf-m-wrap-reverse-on-md {\n flex-wrap: wrap-reverse; }\n .pf-l-flex.pf-m-nowrap-on-md {\n flex-wrap: nowrap; }\n .pf-l-flex.pf-m-justify-content-flex-start-on-md {\n justify-content: flex-start; }\n .pf-l-flex.pf-m-justify-content-flex-end-on-md {\n justify-content: flex-end; }\n .pf-l-flex.pf-m-justify-content-center-on-md {\n justify-content: center; }\n .pf-l-flex.pf-m-justify-content-space-between-on-md {\n justify-content: space-between; }\n .pf-l-flex.pf-m-justify-content-space-around-on-md {\n justify-content: space-around; }\n .pf-l-flex.pf-m-justify-content-space-evenly-on-md {\n justify-content: space-evenly; }\n .pf-l-flex.pf-m-align-items-flex-start-on-md {\n align-items: flex-start; }\n .pf-l-flex.pf-m-align-items-flex-end-on-md {\n align-items: flex-end; }\n .pf-l-flex.pf-m-align-items-center-on-md {\n align-items: center; }\n .pf-l-flex.pf-m-align-items-stretch-on-md {\n align-items: stretch; }\n .pf-l-flex.pf-m-align-items-baseline-on-md {\n align-items: baseline; }\n .pf-l-flex.pf-m-align-content-flex-start-on-md {\n align-content: flex-start; }\n .pf-l-flex.pf-m-align-content-flex-end-on-md {\n align-content: flex-end; }\n .pf-l-flex.pf-m-align-content-center-on-md {\n align-content: center; }\n .pf-l-flex.pf-m-align-content-stretch-on-md {\n align-content: stretch; }\n .pf-l-flex.pf-m-align-content-space-between-on-md {\n align-content: space-between; }\n .pf-l-flex.pf-m-align-content-space-around-on-md {\n align-content: space-around; }\n .pf-l-flex > .pf-m-align-right-on-md {\n margin-left: auto; }\n .pf-l-flex > .pf-m-align-left-on-md {\n margin-left: 0; }\n .pf-l-flex > .pf-m-grow-on-md {\n flex-grow: 1; }\n .pf-l-flex > .pf-m-shrink-on-md {\n flex-shrink: 1; }\n .pf-l-flex > .pf-m-full-width-on-md {\n width: 100%;\n margin-right: 0; }\n .pf-l-flex > .pf-m-flex-1-on-md {\n flex: 1 0 0; }\n .pf-l-flex > .pf-m-flex-2-on-md {\n flex: 2 0 0; }\n .pf-l-flex > .pf-m-flex-3-on-md {\n flex: 3 0 0; }\n .pf-l-flex > .pf-m-flex-4-on-md {\n flex: 4 0 0; }\n .pf-l-flex > .pf-m-flex-default-on-md {\n flex: 0 1 auto; }\n .pf-l-flex > .pf-m-flex-none-on-md {\n flex: none; }\n .pf-l-flex > .pf-m-align-self-flex-start-on-md {\n align-self: flex-start; }\n .pf-l-flex > .pf-m-align-self-flex-end-on-md {\n align-self: flex-end; }\n .pf-l-flex > .pf-m-align-self-center-on-md {\n align-self: center; }\n .pf-l-flex > .pf-m-align-self-baseline-on-md {\n align-self: baseline; }\n .pf-l-flex > .pf-m-align-self-stretch-on-md {\n align-self: stretch; } }\n\n@media (min-width: 992px) {\n .pf-l-flex.pf-m-flex-on-lg {\n display: var(--pf-l-flex--Display); }\n .pf-l-flex.pf-m-inline-flex-on-lg {\n --pf-l-flex--Display: inline-flex; }\n .pf-l-flex.pf-m-column-on-lg {\n flex-direction: column;\n align-items: normal; }\n .pf-l-flex.pf-m-column-on-lg > * {\n margin: 0 0 var(--pf-l-flex--spacer) 0; }\n .pf-l-flex.pf-m-column-reverse-on-lg {\n flex-direction: column-reverse;\n align-items: normal; }\n .pf-l-flex.pf-m-column-reverse-on-lg > * {\n margin: var(--pf-l-flex--spacer) 0 0 0; }\n .pf-l-flex.pf-m-row-on-lg {\n flex-direction: row;\n align-items: var(--pf-l-flex--m-row--AlignItems); }\n .pf-l-flex.pf-m-row-on-lg > * {\n margin: 0 var(--pf-l-flex--spacer) 0 0; }\n .pf-l-flex.pf-m-row-reverse-on-lg {\n flex-direction: row-reverse;\n align-items: var(--pf-l-flex--m-row-reverse--AlignItems); }\n .pf-l-flex.pf-m-row-reverse-on-lg > * {\n margin: 0 0 0 var(--pf-l-flex--spacer); }\n .pf-l-flex.pf-m-wrap-on-lg {\n flex-wrap: wrap; }\n .pf-l-flex.pf-m-wrap-reverse-on-lg {\n flex-wrap: wrap-reverse; }\n .pf-l-flex.pf-m-nowrap-on-lg {\n flex-wrap: nowrap; }\n .pf-l-flex.pf-m-justify-content-flex-start-on-lg {\n justify-content: flex-start; }\n .pf-l-flex.pf-m-justify-content-flex-end-on-lg {\n justify-content: flex-end; }\n .pf-l-flex.pf-m-justify-content-center-on-lg {\n justify-content: center; }\n .pf-l-flex.pf-m-justify-content-space-between-on-lg {\n justify-content: space-between; }\n .pf-l-flex.pf-m-justify-content-space-around-on-lg {\n justify-content: space-around; }\n .pf-l-flex.pf-m-justify-content-space-evenly-on-lg {\n justify-content: space-evenly; }\n .pf-l-flex.pf-m-align-items-flex-start-on-lg {\n align-items: flex-start; }\n .pf-l-flex.pf-m-align-items-flex-end-on-lg {\n align-items: flex-end; }\n .pf-l-flex.pf-m-align-items-center-on-lg {\n align-items: center; }\n .pf-l-flex.pf-m-align-items-stretch-on-lg {\n align-items: stretch; }\n .pf-l-flex.pf-m-align-items-baseline-on-lg {\n align-items: baseline; }\n .pf-l-flex.pf-m-align-content-flex-start-on-lg {\n align-content: flex-start; }\n .pf-l-flex.pf-m-align-content-flex-end-on-lg {\n align-content: flex-end; }\n .pf-l-flex.pf-m-align-content-center-on-lg {\n align-content: center; }\n .pf-l-flex.pf-m-align-content-stretch-on-lg {\n align-content: stretch; }\n .pf-l-flex.pf-m-align-content-space-between-on-lg {\n align-content: space-between; }\n .pf-l-flex.pf-m-align-content-space-around-on-lg {\n align-content: space-around; }\n .pf-l-flex > .pf-m-align-right-on-lg {\n margin-left: auto; }\n .pf-l-flex > .pf-m-align-left-on-lg {\n margin-left: 0; }\n .pf-l-flex > .pf-m-grow-on-lg {\n flex-grow: 1; }\n .pf-l-flex > .pf-m-shrink-on-lg {\n flex-shrink: 1; }\n .pf-l-flex > .pf-m-full-width-on-lg {\n width: 100%;\n margin-right: 0; }\n .pf-l-flex > .pf-m-flex-1-on-lg {\n flex: 1 0 0; }\n .pf-l-flex > .pf-m-flex-2-on-lg {\n flex: 2 0 0; }\n .pf-l-flex > .pf-m-flex-3-on-lg {\n flex: 3 0 0; }\n .pf-l-flex > .pf-m-flex-4-on-lg {\n flex: 4 0 0; }\n .pf-l-flex > .pf-m-flex-default-on-lg {\n flex: 0 1 auto; }\n .pf-l-flex > .pf-m-flex-none-on-lg {\n flex: none; }\n .pf-l-flex > .pf-m-align-self-flex-start-on-lg {\n align-self: flex-start; }\n .pf-l-flex > .pf-m-align-self-flex-end-on-lg {\n align-self: flex-end; }\n .pf-l-flex > .pf-m-align-self-center-on-lg {\n align-self: center; }\n .pf-l-flex > .pf-m-align-self-baseline-on-lg {\n align-self: baseline; }\n .pf-l-flex > .pf-m-align-self-stretch-on-lg {\n align-self: stretch; } }\n\n@media (min-width: 1200px) {\n .pf-l-flex.pf-m-flex-on-xl {\n display: var(--pf-l-flex--Display); }\n .pf-l-flex.pf-m-inline-flex-on-xl {\n --pf-l-flex--Display: inline-flex; }\n .pf-l-flex.pf-m-column-on-xl {\n flex-direction: column;\n align-items: normal; }\n .pf-l-flex.pf-m-column-on-xl > * {\n margin: 0 0 var(--pf-l-flex--spacer) 0; }\n .pf-l-flex.pf-m-column-reverse-on-xl {\n flex-direction: column-reverse;\n align-items: normal; }\n .pf-l-flex.pf-m-column-reverse-on-xl > * {\n margin: var(--pf-l-flex--spacer) 0 0 0; }\n .pf-l-flex.pf-m-row-on-xl {\n flex-direction: row;\n align-items: var(--pf-l-flex--m-row--AlignItems); }\n .pf-l-flex.pf-m-row-on-xl > * {\n margin: 0 var(--pf-l-flex--spacer) 0 0; }\n .pf-l-flex.pf-m-row-reverse-on-xl {\n flex-direction: row-reverse;\n align-items: var(--pf-l-flex--m-row-reverse--AlignItems); }\n .pf-l-flex.pf-m-row-reverse-on-xl > * {\n margin: 0 0 0 var(--pf-l-flex--spacer); }\n .pf-l-flex.pf-m-wrap-on-xl {\n flex-wrap: wrap; }\n .pf-l-flex.pf-m-wrap-reverse-on-xl {\n flex-wrap: wrap-reverse; }\n .pf-l-flex.pf-m-nowrap-on-xl {\n flex-wrap: nowrap; }\n .pf-l-flex.pf-m-justify-content-flex-start-on-xl {\n justify-content: flex-start; }\n .pf-l-flex.pf-m-justify-content-flex-end-on-xl {\n justify-content: flex-end; }\n .pf-l-flex.pf-m-justify-content-center-on-xl {\n justify-content: center; }\n .pf-l-flex.pf-m-justify-content-space-between-on-xl {\n justify-content: space-between; }\n .pf-l-flex.pf-m-justify-content-space-around-on-xl {\n justify-content: space-around; }\n .pf-l-flex.pf-m-justify-content-space-evenly-on-xl {\n justify-content: space-evenly; }\n .pf-l-flex.pf-m-align-items-flex-start-on-xl {\n align-items: flex-start; }\n .pf-l-flex.pf-m-align-items-flex-end-on-xl {\n align-items: flex-end; }\n .pf-l-flex.pf-m-align-items-center-on-xl {\n align-items: center; }\n .pf-l-flex.pf-m-align-items-stretch-on-xl {\n align-items: stretch; }\n .pf-l-flex.pf-m-align-items-baseline-on-xl {\n align-items: baseline; }\n .pf-l-flex.pf-m-align-content-flex-start-on-xl {\n align-content: flex-start; }\n .pf-l-flex.pf-m-align-content-flex-end-on-xl {\n align-content: flex-end; }\n .pf-l-flex.pf-m-align-content-center-on-xl {\n align-content: center; }\n .pf-l-flex.pf-m-align-content-stretch-on-xl {\n align-content: stretch; }\n .pf-l-flex.pf-m-align-content-space-between-on-xl {\n align-content: space-between; }\n .pf-l-flex.pf-m-align-content-space-around-on-xl {\n align-content: space-around; }\n .pf-l-flex > .pf-m-align-right-on-xl {\n margin-left: auto; }\n .pf-l-flex > .pf-m-align-left-on-xl {\n margin-left: 0; }\n .pf-l-flex > .pf-m-grow-on-xl {\n flex-grow: 1; }\n .pf-l-flex > .pf-m-shrink-on-xl {\n flex-shrink: 1; }\n .pf-l-flex > .pf-m-full-width-on-xl {\n width: 100%;\n margin-right: 0; }\n .pf-l-flex > .pf-m-flex-1-on-xl {\n flex: 1 0 0; }\n .pf-l-flex > .pf-m-flex-2-on-xl {\n flex: 2 0 0; }\n .pf-l-flex > .pf-m-flex-3-on-xl {\n flex: 3 0 0; }\n .pf-l-flex > .pf-m-flex-4-on-xl {\n flex: 4 0 0; }\n .pf-l-flex > .pf-m-flex-default-on-xl {\n flex: 0 1 auto; }\n .pf-l-flex > .pf-m-flex-none-on-xl {\n flex: none; }\n .pf-l-flex > .pf-m-align-self-flex-start-on-xl {\n align-self: flex-start; }\n .pf-l-flex > .pf-m-align-self-flex-end-on-xl {\n align-self: flex-end; }\n .pf-l-flex > .pf-m-align-self-center-on-xl {\n align-self: center; }\n .pf-l-flex > .pf-m-align-self-baseline-on-xl {\n align-self: baseline; }\n .pf-l-flex > .pf-m-align-self-stretch-on-xl {\n align-self: stretch; } }\n\n@media (min-width: 1450px) {\n .pf-l-flex.pf-m-flex-on-2xl {\n display: var(--pf-l-flex--Display); }\n .pf-l-flex.pf-m-inline-flex-on-2xl {\n --pf-l-flex--Display: inline-flex; }\n .pf-l-flex.pf-m-column-on-2xl {\n flex-direction: column;\n align-items: normal; }\n .pf-l-flex.pf-m-column-on-2xl > * {\n margin: 0 0 var(--pf-l-flex--spacer) 0; }\n .pf-l-flex.pf-m-column-reverse-on-2xl {\n flex-direction: column-reverse;\n align-items: normal; }\n .pf-l-flex.pf-m-column-reverse-on-2xl > * {\n margin: var(--pf-l-flex--spacer) 0 0 0; }\n .pf-l-flex.pf-m-row-on-2xl {\n flex-direction: row;\n align-items: var(--pf-l-flex--m-row--AlignItems); }\n .pf-l-flex.pf-m-row-on-2xl > * {\n margin: 0 var(--pf-l-flex--spacer) 0 0; }\n .pf-l-flex.pf-m-row-reverse-on-2xl {\n flex-direction: row-reverse;\n align-items: var(--pf-l-flex--m-row-reverse--AlignItems); }\n .pf-l-flex.pf-m-row-reverse-on-2xl > * {\n margin: 0 0 0 var(--pf-l-flex--spacer); }\n .pf-l-flex.pf-m-wrap-on-2xl {\n flex-wrap: wrap; }\n .pf-l-flex.pf-m-wrap-reverse-on-2xl {\n flex-wrap: wrap-reverse; }\n .pf-l-flex.pf-m-nowrap-on-2xl {\n flex-wrap: nowrap; }\n .pf-l-flex.pf-m-justify-content-flex-start-on-2xl {\n justify-content: flex-start; }\n .pf-l-flex.pf-m-justify-content-flex-end-on-2xl {\n justify-content: flex-end; }\n .pf-l-flex.pf-m-justify-content-center-on-2xl {\n justify-content: center; }\n .pf-l-flex.pf-m-justify-content-space-between-on-2xl {\n justify-content: space-between; }\n .pf-l-flex.pf-m-justify-content-space-around-on-2xl {\n justify-content: space-around; }\n .pf-l-flex.pf-m-justify-content-space-evenly-on-2xl {\n justify-content: space-evenly; }\n .pf-l-flex.pf-m-align-items-flex-start-on-2xl {\n align-items: flex-start; }\n .pf-l-flex.pf-m-align-items-flex-end-on-2xl {\n align-items: flex-end; }\n .pf-l-flex.pf-m-align-items-center-on-2xl {\n align-items: center; }\n .pf-l-flex.pf-m-align-items-stretch-on-2xl {\n align-items: stretch; }\n .pf-l-flex.pf-m-align-items-baseline-on-2xl {\n align-items: baseline; }\n .pf-l-flex.pf-m-align-content-flex-start-on-2xl {\n align-content: flex-start; }\n .pf-l-flex.pf-m-align-content-flex-end-on-2xl {\n align-content: flex-end; }\n .pf-l-flex.pf-m-align-content-center-on-2xl {\n align-content: center; }\n .pf-l-flex.pf-m-align-content-stretch-on-2xl {\n align-content: stretch; }\n .pf-l-flex.pf-m-align-content-space-between-on-2xl {\n align-content: space-between; }\n .pf-l-flex.pf-m-align-content-space-around-on-2xl {\n align-content: space-around; }\n .pf-l-flex > .pf-m-align-right-on-2xl {\n margin-left: auto; }\n .pf-l-flex > .pf-m-align-left-on-2xl {\n margin-left: 0; }\n .pf-l-flex > .pf-m-grow-on-2xl {\n flex-grow: 1; }\n .pf-l-flex > .pf-m-shrink-on-2xl {\n flex-shrink: 1; }\n .pf-l-flex > .pf-m-full-width-on-2xl {\n width: 100%;\n margin-right: 0; }\n .pf-l-flex > .pf-m-flex-1-on-2xl {\n flex: 1 0 0; }\n .pf-l-flex > .pf-m-flex-2-on-2xl {\n flex: 2 0 0; }\n .pf-l-flex > .pf-m-flex-3-on-2xl {\n flex: 3 0 0; }\n .pf-l-flex > .pf-m-flex-4-on-2xl {\n flex: 4 0 0; }\n .pf-l-flex > .pf-m-flex-default-on-2xl {\n flex: 0 1 auto; }\n .pf-l-flex > .pf-m-flex-none-on-2xl {\n flex: none; }\n .pf-l-flex > .pf-m-align-self-flex-start-on-2xl {\n align-self: flex-start; }\n .pf-l-flex > .pf-m-align-self-flex-end-on-2xl {\n align-self: flex-end; }\n .pf-l-flex > .pf-m-align-self-center-on-2xl {\n align-self: center; }\n .pf-l-flex > .pf-m-align-self-baseline-on-2xl {\n align-self: baseline; }\n .pf-l-flex > .pf-m-align-self-stretch-on-2xl {\n align-self: stretch; } }\n\n.pf-l-flex.pf-m-space-items-none > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n\n.pf-l-flex.pf-m-space-items-none > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-xs > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n\n.pf-l-flex.pf-m-space-items-xs > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n\n.pf-l-flex.pf-m-space-items-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n\n.pf-l-flex.pf-m-space-items-md > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n\n.pf-l-flex.pf-m-space-items-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n\n.pf-l-flex.pf-m-space-items-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n\n.pf-l-flex.pf-m-space-items-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-3xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n\n.pf-l-flex.pf-m-space-items-3xl > :last-child {\n --pf-l-flex--spacer: 0; }\n\n.pf-l-flex.pf-m-space-items-4xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n\n.pf-l-flex.pf-m-space-items-4xl > :last-child {\n --pf-l-flex--spacer: 0; }\n\n@media (min-width: 576px) {\n .pf-l-flex.pf-m-space-items-none-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex.pf-m-space-items-none-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xs-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex.pf-m-space-items-xs-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-sm-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex.pf-m-space-items-sm-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-md-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex.pf-m-space-items-md-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-lg-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex.pf-m-space-items-lg-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xl-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex.pf-m-space-items-xl-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-2xl-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex.pf-m-space-items-2xl-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-3xl-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex.pf-m-space-items-3xl-on-sm > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-4xl-on-sm > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex.pf-m-space-items-4xl-on-sm > :last-child {\n --pf-l-flex--spacer: 0; } }\n\n@media (min-width: 768px) {\n .pf-l-flex.pf-m-space-items-none-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex.pf-m-space-items-none-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xs-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex.pf-m-space-items-xs-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-sm-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex.pf-m-space-items-sm-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-md-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex.pf-m-space-items-md-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-lg-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex.pf-m-space-items-lg-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xl-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex.pf-m-space-items-xl-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-2xl-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex.pf-m-space-items-2xl-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-3xl-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex.pf-m-space-items-3xl-on-md > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-4xl-on-md > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex.pf-m-space-items-4xl-on-md > :last-child {\n --pf-l-flex--spacer: 0; } }\n\n@media (min-width: 992px) {\n .pf-l-flex.pf-m-space-items-none-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex.pf-m-space-items-none-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xs-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex.pf-m-space-items-xs-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-sm-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex.pf-m-space-items-sm-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-md-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex.pf-m-space-items-md-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-lg-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex.pf-m-space-items-lg-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xl-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex.pf-m-space-items-xl-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-2xl-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex.pf-m-space-items-2xl-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-3xl-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex.pf-m-space-items-3xl-on-lg > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-4xl-on-lg > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex.pf-m-space-items-4xl-on-lg > :last-child {\n --pf-l-flex--spacer: 0; } }\n\n@media (min-width: 1200px) {\n .pf-l-flex.pf-m-space-items-none-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex.pf-m-space-items-none-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xs-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex.pf-m-space-items-xs-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-sm-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex.pf-m-space-items-sm-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-md-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex.pf-m-space-items-md-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-lg-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex.pf-m-space-items-lg-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xl-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex.pf-m-space-items-xl-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-2xl-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex.pf-m-space-items-2xl-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-3xl-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex.pf-m-space-items-3xl-on-xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-4xl-on-xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex.pf-m-space-items-4xl-on-xl > :last-child {\n --pf-l-flex--spacer: 0; } }\n\n@media (min-width: 1450px) {\n .pf-l-flex.pf-m-space-items-none-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex.pf-m-space-items-none-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xs-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex.pf-m-space-items-xs-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-sm-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex.pf-m-space-items-sm-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-md-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex.pf-m-space-items-md-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-lg-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex.pf-m-space-items-lg-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-xl-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex.pf-m-space-items-xl-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-2xl-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex.pf-m-space-items-2xl-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-3xl-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex.pf-m-space-items-3xl-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; }\n .pf-l-flex.pf-m-space-items-4xl-on-2xl > * {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex.pf-m-space-items-4xl-on-2xl > :last-child {\n --pf-l-flex--spacer: 0; } }\n\n.pf-l-flex .pf-m-spacer-none {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-none:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n\n.pf-l-flex .pf-m-spacer-xs {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-xs:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n\n.pf-l-flex .pf-m-spacer-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n\n.pf-l-flex .pf-m-spacer-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n\n.pf-l-flex .pf-m-spacer-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n\n.pf-l-flex .pf-m-spacer-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n\n.pf-l-flex .pf-m-spacer-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n\n.pf-l-flex .pf-m-spacer-3xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-3xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n\n.pf-l-flex .pf-m-spacer-4xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex .pf-m-spacer-4xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n\n@media (min-width: 576px) {\n .pf-l-flex .pf-m-spacer-none-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-none-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-xs-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-xs-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-sm-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-sm-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-md-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-md-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-lg-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-lg-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-xl-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-xl-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-sm {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-sm:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); } }\n\n@media (min-width: 768px) {\n .pf-l-flex .pf-m-spacer-none-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-none-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-xs-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-xs-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-sm-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-sm-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-md-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-md-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-lg-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-lg-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-xl-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-xl-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-md {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-md:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); } }\n\n@media (min-width: 992px) {\n .pf-l-flex .pf-m-spacer-none-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-none-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-xs-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-xs-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-sm-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-sm-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-md-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-md-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-lg-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-lg-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-xl-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-xl-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-lg {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-lg:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); } }\n\n@media (min-width: 1200px) {\n .pf-l-flex .pf-m-spacer-none-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-none-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-xs-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-xs-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-sm-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-sm-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-md-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-md-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-lg-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-lg-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-xl-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-xl-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); } }\n\n@media (min-width: 1450px) {\n .pf-l-flex .pf-m-spacer-none-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-none-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--none); }\n .pf-l-flex .pf-m-spacer-xs-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-xs-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xs); }\n .pf-l-flex .pf-m-spacer-sm-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-sm-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--sm); }\n .pf-l-flex .pf-m-spacer-md-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-md-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--md); }\n .pf-l-flex .pf-m-spacer-lg-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-lg-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--lg); }\n .pf-l-flex .pf-m-spacer-xl-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-xl-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-2xl-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--2xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-3xl-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--3xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-2xl {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); }\n .pf-l-flex .pf-m-spacer-4xl-on-2xl:last-child {\n --pf-l-flex--spacer: var(--pf-l-flex--spacer--4xl); } }\n\n.pf-l-gallery {\n --pf-l-gallery--m-gutter--GridGap: var(--pf-global--gutter);\n --pf-l-gallery--GridTemplateColumns--min: 250px;\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min);\n --pf-l-gallery--GridTemplateColumns--max: 1fr;\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max);\n --pf-l-gallery--GridTemplateColumns: repeat(auto-fill, minmax(var(--pf-l-gallery--GridTemplateColumns--minmax--min), var(--pf-l-gallery--GridTemplateColumns--minmax--max)));\n --pf-l-gallery--GridTemplateRows: auto;\n display: grid;\n grid-template-columns: var(--pf-l-gallery--GridTemplateColumns);\n grid-template-rows: var(--pf-l-gallery--GridTemplateRows);\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min);\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max); }\n .pf-l-gallery.pf-m-gutter {\n grid-gap: var(--pf-l-gallery--m-gutter--GridGap); }\n @media (min-width: 576px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min-on-sm, var(--pf-l-gallery--GridTemplateColumns--min)); } }\n @media (min-width: 768px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min-on-md, var(--pf-l-gallery--GridTemplateColumns--min-on-sm, var(--pf-l-gallery--GridTemplateColumns--min))); } }\n @media (min-width: 992px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min-on-lg, var(--pf-l-gallery--GridTemplateColumns--min-on-md, var(--pf-l-gallery--GridTemplateColumns--min-on-sm, var(--pf-l-gallery--GridTemplateColumns--min)))); } }\n @media (min-width: 1200px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min-on-xl, var(--pf-l-gallery--GridTemplateColumns--min-on-lg, var(--pf-l-gallery--GridTemplateColumns--min-on-md, var(--pf-l-gallery--GridTemplateColumns--min-on-sm, var(--pf-l-gallery--GridTemplateColumns--min))))); } }\n @media (min-width: 1450px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--min: var(--pf-l-gallery--GridTemplateColumns--min-on-2xl, var(--pf-l-gallery--GridTemplateColumns--min-on-xl, var(--pf-l-gallery--GridTemplateColumns--min-on-lg, var(--pf-l-gallery--GridTemplateColumns--min-on-md, var(--pf-l-gallery--GridTemplateColumns--min-on-sm, var(--pf-l-gallery--GridTemplateColumns--min)))))); } }\n @media (min-width: 576px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max-on-sm, var(--pf-l-gallery--GridTemplateColumns--max)); } }\n @media (min-width: 768px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max-on-md, var(--pf-l-gallery--GridTemplateColumns--max-on-sm, var(--pf-l-gallery--GridTemplateColumns--max))); } }\n @media (min-width: 992px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max-on-lg, var(--pf-l-gallery--GridTemplateColumns--max-on-md, var(--pf-l-gallery--GridTemplateColumns--max-on-sm, var(--pf-l-gallery--GridTemplateColumns--max)))); } }\n @media (min-width: 1200px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max-on-xl, var(--pf-l-gallery--GridTemplateColumns--max-on-lg, var(--pf-l-gallery--GridTemplateColumns--max-on-md, var(--pf-l-gallery--GridTemplateColumns--max-on-sm, var(--pf-l-gallery--GridTemplateColumns--max))))); } }\n @media (min-width: 1450px) {\n .pf-l-gallery {\n --pf-l-gallery--GridTemplateColumns--minmax--max: var(--pf-l-gallery--GridTemplateColumns--max-on-2xl, var(--pf-l-gallery--GridTemplateColumns--max-on-xl, var(--pf-l-gallery--GridTemplateColumns--max-on-lg, var(--pf-l-gallery--GridTemplateColumns--max-on-md, var(--pf-l-gallery--GridTemplateColumns--max-on-sm, var(--pf-l-gallery--GridTemplateColumns--max)))))); } }\n\n.pf-l-grid {\n --pf-l-grid--m-gutter--GridGap: var(--pf-global--gutter);\n --pf-l-grid__item--GridColumnStart: auto;\n --pf-l-grid__item--GridColumnEnd: span 12;\n --pf-l-grid--item--Order: 0;\n display: grid;\n grid-template-columns: repeat(12, [col-start] 1fr); }\n .pf-l-grid > *,\n .pf-l-grid .pf-l-grid__item {\n min-width: 0;\n min-height: 0;\n grid-column-start: var(--pf-l-grid__item--GridColumnStart);\n grid-column-end: var(--pf-l-grid__item--GridColumnEnd);\n order: var(--pf-l-grid--item--Order); }\n @media (min-width: 576px) {\n .pf-l-grid > *,\n .pf-l-grid .pf-l-grid__item {\n order: var(--pf-l-grid--item--Order-on-sm, var(--pf-l-grid--item--Order)); } }\n @media (min-width: 768px) {\n .pf-l-grid > *,\n .pf-l-grid .pf-l-grid__item {\n order: var(--pf-l-grid--item--Order-on-md, var(--pf-l-grid--item--Order-on-sm, var(--pf-l-grid--item--Order))); } }\n @media (min-width: 992px) {\n .pf-l-grid > *,\n .pf-l-grid .pf-l-grid__item {\n order: var(--pf-l-grid--item--Order-on-lg, var(--pf-l-grid--item--Order-on-md, var(--pf-l-grid--item--Order-on-sm, var(--pf-l-grid--item--Order)))); } }\n @media (min-width: 1200px) {\n .pf-l-grid > *,\n .pf-l-grid .pf-l-grid__item {\n order: var(--pf-l-grid--item--Order-on-xl, var(--pf-l-grid--item--Order-on-lg, var(--pf-l-grid--item--Order-on-md, var(--pf-l-grid--item--Order-on-sm, var(--pf-l-grid--item--Order))))); } }\n @media (min-width: 1450px) {\n .pf-l-grid > *,\n .pf-l-grid .pf-l-grid__item {\n order: var(--pf-l-grid--item--Order-on-2xl, var(--pf-l-grid--item--Order-on-xl, var(--pf-l-grid--item--Order-on-lg, var(--pf-l-grid--item--Order-on-md, var(--pf-l-grid--item--Order-on-sm, var(--pf-l-grid--item--Order)))))); } }\n .pf-l-grid.pf-m-all-1-col > * {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid.pf-m-all-2-col > * {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid.pf-m-all-3-col > * {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid.pf-m-all-4-col > * {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid.pf-m-all-5-col > * {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid.pf-m-all-6-col > * {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid.pf-m-all-7-col > * {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid.pf-m-all-8-col > * {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid.pf-m-all-9-col > * {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid.pf-m-all-10-col > * {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid.pf-m-all-11-col > * {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid.pf-m-all-12-col > * {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n @media screen and (min-width: 576px) {\n .pf-l-grid.pf-m-all-1-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid.pf-m-all-2-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid.pf-m-all-3-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid.pf-m-all-4-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid.pf-m-all-5-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid.pf-m-all-6-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid.pf-m-all-7-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid.pf-m-all-8-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid.pf-m-all-9-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid.pf-m-all-10-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid.pf-m-all-11-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid.pf-m-all-12-col-on-sm > * {\n --pf-l-grid__item--GridColumnEnd: span 12; } }\n @media screen and (min-width: 768px) {\n .pf-l-grid.pf-m-all-1-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid.pf-m-all-2-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid.pf-m-all-3-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid.pf-m-all-4-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid.pf-m-all-5-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid.pf-m-all-6-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid.pf-m-all-7-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid.pf-m-all-8-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid.pf-m-all-9-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid.pf-m-all-10-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid.pf-m-all-11-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid.pf-m-all-12-col-on-md > * {\n --pf-l-grid__item--GridColumnEnd: span 12; } }\n @media screen and (min-width: 992px) {\n .pf-l-grid.pf-m-all-1-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid.pf-m-all-2-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid.pf-m-all-3-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid.pf-m-all-4-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid.pf-m-all-5-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid.pf-m-all-6-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid.pf-m-all-7-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid.pf-m-all-8-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid.pf-m-all-9-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid.pf-m-all-10-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid.pf-m-all-11-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid.pf-m-all-12-col-on-lg > * {\n --pf-l-grid__item--GridColumnEnd: span 12; } }\n @media screen and (min-width: 1200px) {\n .pf-l-grid.pf-m-all-1-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid.pf-m-all-2-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid.pf-m-all-3-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid.pf-m-all-4-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid.pf-m-all-5-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid.pf-m-all-6-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid.pf-m-all-7-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid.pf-m-all-8-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid.pf-m-all-9-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid.pf-m-all-10-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid.pf-m-all-11-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid.pf-m-all-12-col-on-xl > * {\n --pf-l-grid__item--GridColumnEnd: span 12; } }\n @media screen and (min-width: 1450px) {\n .pf-l-grid.pf-m-all-1-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid.pf-m-all-2-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid.pf-m-all-3-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid.pf-m-all-4-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid.pf-m-all-5-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid.pf-m-all-6-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid.pf-m-all-7-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid.pf-m-all-8-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid.pf-m-all-9-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid.pf-m-all-10-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid.pf-m-all-11-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid.pf-m-all-12-col-on-2xl > * {\n --pf-l-grid__item--GridColumnEnd: span 12; } }\n .pf-l-grid > .pf-m-1-col {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid > .pf-m-2-col {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid > .pf-m-3-col {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid > .pf-m-4-col {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid > .pf-m-5-col {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid > .pf-m-6-col {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid > .pf-m-7-col {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid > .pf-m-8-col {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid > .pf-m-9-col {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid > .pf-m-10-col {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid > .pf-m-11-col {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid > .pf-m-12-col {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n .pf-l-grid > .pf-m-offset-1-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(1 + 1); }\n .pf-l-grid > .pf-m-offset-2-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(2 + 1); }\n .pf-l-grid > .pf-m-offset-3-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(3 + 1); }\n .pf-l-grid > .pf-m-offset-4-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(4 + 1); }\n .pf-l-grid > .pf-m-offset-5-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(5 + 1); }\n .pf-l-grid > .pf-m-offset-6-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(6 + 1); }\n .pf-l-grid > .pf-m-offset-7-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(7 + 1); }\n .pf-l-grid > .pf-m-offset-8-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(8 + 1); }\n .pf-l-grid > .pf-m-offset-9-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(9 + 1); }\n .pf-l-grid > .pf-m-offset-10-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(10 + 1); }\n .pf-l-grid > .pf-m-offset-11-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(11 + 1); }\n .pf-l-grid > .pf-m-offset-12-col {\n --pf-l-grid__item--GridColumnStart: col-start calc(12 + 1); }\n .pf-l-grid > .pf-m-1-row {\n grid-row: span 1; }\n .pf-l-grid > .pf-m-2-row {\n grid-row: span 2; }\n .pf-l-grid > .pf-m-3-row {\n grid-row: span 3; }\n .pf-l-grid > .pf-m-4-row {\n grid-row: span 4; }\n .pf-l-grid > .pf-m-5-row {\n grid-row: span 5; }\n .pf-l-grid > .pf-m-6-row {\n grid-row: span 6; }\n .pf-l-grid > .pf-m-7-row {\n grid-row: span 7; }\n .pf-l-grid > .pf-m-8-row {\n grid-row: span 8; }\n .pf-l-grid > .pf-m-9-row {\n grid-row: span 9; }\n .pf-l-grid > .pf-m-10-row {\n grid-row: span 10; }\n .pf-l-grid > .pf-m-11-row {\n grid-row: span 11; }\n .pf-l-grid > .pf-m-12-row {\n grid-row: span 12; }\n @media screen and (min-width: 576px) {\n .pf-l-grid > .pf-m-1-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid > .pf-m-2-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid > .pf-m-3-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid > .pf-m-4-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid > .pf-m-5-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid > .pf-m-6-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid > .pf-m-7-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid > .pf-m-8-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid > .pf-m-9-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid > .pf-m-10-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid > .pf-m-11-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid > .pf-m-12-col-on-sm {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n .pf-l-grid > .pf-m-offset-1-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(1 + 1); }\n .pf-l-grid > .pf-m-offset-2-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(2 + 1); }\n .pf-l-grid > .pf-m-offset-3-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(3 + 1); }\n .pf-l-grid > .pf-m-offset-4-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(4 + 1); }\n .pf-l-grid > .pf-m-offset-5-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(5 + 1); }\n .pf-l-grid > .pf-m-offset-6-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(6 + 1); }\n .pf-l-grid > .pf-m-offset-7-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(7 + 1); }\n .pf-l-grid > .pf-m-offset-8-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(8 + 1); }\n .pf-l-grid > .pf-m-offset-9-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(9 + 1); }\n .pf-l-grid > .pf-m-offset-10-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(10 + 1); }\n .pf-l-grid > .pf-m-offset-11-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(11 + 1); }\n .pf-l-grid > .pf-m-offset-12-col-on-sm {\n --pf-l-grid__item--GridColumnStart: col-start calc(12 + 1); }\n .pf-l-grid > .pf-m-1-row-on-sm {\n grid-row: span 1; }\n .pf-l-grid > .pf-m-2-row-on-sm {\n grid-row: span 2; }\n .pf-l-grid > .pf-m-3-row-on-sm {\n grid-row: span 3; }\n .pf-l-grid > .pf-m-4-row-on-sm {\n grid-row: span 4; }\n .pf-l-grid > .pf-m-5-row-on-sm {\n grid-row: span 5; }\n .pf-l-grid > .pf-m-6-row-on-sm {\n grid-row: span 6; }\n .pf-l-grid > .pf-m-7-row-on-sm {\n grid-row: span 7; }\n .pf-l-grid > .pf-m-8-row-on-sm {\n grid-row: span 8; }\n .pf-l-grid > .pf-m-9-row-on-sm {\n grid-row: span 9; }\n .pf-l-grid > .pf-m-10-row-on-sm {\n grid-row: span 10; }\n .pf-l-grid > .pf-m-11-row-on-sm {\n grid-row: span 11; }\n .pf-l-grid > .pf-m-12-row-on-sm {\n grid-row: span 12; } }\n @media screen and (min-width: 768px) {\n .pf-l-grid > .pf-m-1-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid > .pf-m-2-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid > .pf-m-3-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid > .pf-m-4-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid > .pf-m-5-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid > .pf-m-6-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid > .pf-m-7-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid > .pf-m-8-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid > .pf-m-9-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid > .pf-m-10-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid > .pf-m-11-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid > .pf-m-12-col-on-md {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n .pf-l-grid > .pf-m-offset-1-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(1 + 1); }\n .pf-l-grid > .pf-m-offset-2-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(2 + 1); }\n .pf-l-grid > .pf-m-offset-3-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(3 + 1); }\n .pf-l-grid > .pf-m-offset-4-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(4 + 1); }\n .pf-l-grid > .pf-m-offset-5-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(5 + 1); }\n .pf-l-grid > .pf-m-offset-6-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(6 + 1); }\n .pf-l-grid > .pf-m-offset-7-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(7 + 1); }\n .pf-l-grid > .pf-m-offset-8-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(8 + 1); }\n .pf-l-grid > .pf-m-offset-9-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(9 + 1); }\n .pf-l-grid > .pf-m-offset-10-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(10 + 1); }\n .pf-l-grid > .pf-m-offset-11-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(11 + 1); }\n .pf-l-grid > .pf-m-offset-12-col-on-md {\n --pf-l-grid__item--GridColumnStart: col-start calc(12 + 1); }\n .pf-l-grid > .pf-m-1-row-on-md {\n grid-row: span 1; }\n .pf-l-grid > .pf-m-2-row-on-md {\n grid-row: span 2; }\n .pf-l-grid > .pf-m-3-row-on-md {\n grid-row: span 3; }\n .pf-l-grid > .pf-m-4-row-on-md {\n grid-row: span 4; }\n .pf-l-grid > .pf-m-5-row-on-md {\n grid-row: span 5; }\n .pf-l-grid > .pf-m-6-row-on-md {\n grid-row: span 6; }\n .pf-l-grid > .pf-m-7-row-on-md {\n grid-row: span 7; }\n .pf-l-grid > .pf-m-8-row-on-md {\n grid-row: span 8; }\n .pf-l-grid > .pf-m-9-row-on-md {\n grid-row: span 9; }\n .pf-l-grid > .pf-m-10-row-on-md {\n grid-row: span 10; }\n .pf-l-grid > .pf-m-11-row-on-md {\n grid-row: span 11; }\n .pf-l-grid > .pf-m-12-row-on-md {\n grid-row: span 12; } }\n @media screen and (min-width: 992px) {\n .pf-l-grid > .pf-m-1-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid > .pf-m-2-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid > .pf-m-3-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid > .pf-m-4-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid > .pf-m-5-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid > .pf-m-6-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid > .pf-m-7-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid > .pf-m-8-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid > .pf-m-9-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid > .pf-m-10-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid > .pf-m-11-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid > .pf-m-12-col-on-lg {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n .pf-l-grid > .pf-m-offset-1-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(1 + 1); }\n .pf-l-grid > .pf-m-offset-2-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(2 + 1); }\n .pf-l-grid > .pf-m-offset-3-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(3 + 1); }\n .pf-l-grid > .pf-m-offset-4-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(4 + 1); }\n .pf-l-grid > .pf-m-offset-5-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(5 + 1); }\n .pf-l-grid > .pf-m-offset-6-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(6 + 1); }\n .pf-l-grid > .pf-m-offset-7-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(7 + 1); }\n .pf-l-grid > .pf-m-offset-8-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(8 + 1); }\n .pf-l-grid > .pf-m-offset-9-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(9 + 1); }\n .pf-l-grid > .pf-m-offset-10-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(10 + 1); }\n .pf-l-grid > .pf-m-offset-11-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(11 + 1); }\n .pf-l-grid > .pf-m-offset-12-col-on-lg {\n --pf-l-grid__item--GridColumnStart: col-start calc(12 + 1); }\n .pf-l-grid > .pf-m-1-row-on-lg {\n grid-row: span 1; }\n .pf-l-grid > .pf-m-2-row-on-lg {\n grid-row: span 2; }\n .pf-l-grid > .pf-m-3-row-on-lg {\n grid-row: span 3; }\n .pf-l-grid > .pf-m-4-row-on-lg {\n grid-row: span 4; }\n .pf-l-grid > .pf-m-5-row-on-lg {\n grid-row: span 5; }\n .pf-l-grid > .pf-m-6-row-on-lg {\n grid-row: span 6; }\n .pf-l-grid > .pf-m-7-row-on-lg {\n grid-row: span 7; }\n .pf-l-grid > .pf-m-8-row-on-lg {\n grid-row: span 8; }\n .pf-l-grid > .pf-m-9-row-on-lg {\n grid-row: span 9; }\n .pf-l-grid > .pf-m-10-row-on-lg {\n grid-row: span 10; }\n .pf-l-grid > .pf-m-11-row-on-lg {\n grid-row: span 11; }\n .pf-l-grid > .pf-m-12-row-on-lg {\n grid-row: span 12; } }\n @media screen and (min-width: 1200px) {\n .pf-l-grid > .pf-m-1-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid > .pf-m-2-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid > .pf-m-3-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid > .pf-m-4-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid > .pf-m-5-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid > .pf-m-6-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid > .pf-m-7-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid > .pf-m-8-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid > .pf-m-9-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid > .pf-m-10-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid > .pf-m-11-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid > .pf-m-12-col-on-xl {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n .pf-l-grid > .pf-m-offset-1-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(1 + 1); }\n .pf-l-grid > .pf-m-offset-2-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(2 + 1); }\n .pf-l-grid > .pf-m-offset-3-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(3 + 1); }\n .pf-l-grid > .pf-m-offset-4-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(4 + 1); }\n .pf-l-grid > .pf-m-offset-5-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(5 + 1); }\n .pf-l-grid > .pf-m-offset-6-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(6 + 1); }\n .pf-l-grid > .pf-m-offset-7-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(7 + 1); }\n .pf-l-grid > .pf-m-offset-8-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(8 + 1); }\n .pf-l-grid > .pf-m-offset-9-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(9 + 1); }\n .pf-l-grid > .pf-m-offset-10-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(10 + 1); }\n .pf-l-grid > .pf-m-offset-11-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(11 + 1); }\n .pf-l-grid > .pf-m-offset-12-col-on-xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(12 + 1); }\n .pf-l-grid > .pf-m-1-row-on-xl {\n grid-row: span 1; }\n .pf-l-grid > .pf-m-2-row-on-xl {\n grid-row: span 2; }\n .pf-l-grid > .pf-m-3-row-on-xl {\n grid-row: span 3; }\n .pf-l-grid > .pf-m-4-row-on-xl {\n grid-row: span 4; }\n .pf-l-grid > .pf-m-5-row-on-xl {\n grid-row: span 5; }\n .pf-l-grid > .pf-m-6-row-on-xl {\n grid-row: span 6; }\n .pf-l-grid > .pf-m-7-row-on-xl {\n grid-row: span 7; }\n .pf-l-grid > .pf-m-8-row-on-xl {\n grid-row: span 8; }\n .pf-l-grid > .pf-m-9-row-on-xl {\n grid-row: span 9; }\n .pf-l-grid > .pf-m-10-row-on-xl {\n grid-row: span 10; }\n .pf-l-grid > .pf-m-11-row-on-xl {\n grid-row: span 11; }\n .pf-l-grid > .pf-m-12-row-on-xl {\n grid-row: span 12; } }\n @media screen and (min-width: 1450px) {\n .pf-l-grid > .pf-m-1-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 1; }\n .pf-l-grid > .pf-m-2-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 2; }\n .pf-l-grid > .pf-m-3-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 3; }\n .pf-l-grid > .pf-m-4-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 4; }\n .pf-l-grid > .pf-m-5-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 5; }\n .pf-l-grid > .pf-m-6-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 6; }\n .pf-l-grid > .pf-m-7-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 7; }\n .pf-l-grid > .pf-m-8-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 8; }\n .pf-l-grid > .pf-m-9-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 9; }\n .pf-l-grid > .pf-m-10-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 10; }\n .pf-l-grid > .pf-m-11-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 11; }\n .pf-l-grid > .pf-m-12-col-on-2xl {\n --pf-l-grid__item--GridColumnEnd: span 12; }\n .pf-l-grid > .pf-m-offset-1-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(1 + 1); }\n .pf-l-grid > .pf-m-offset-2-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(2 + 1); }\n .pf-l-grid > .pf-m-offset-3-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(3 + 1); }\n .pf-l-grid > .pf-m-offset-4-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(4 + 1); }\n .pf-l-grid > .pf-m-offset-5-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(5 + 1); }\n .pf-l-grid > .pf-m-offset-6-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(6 + 1); }\n .pf-l-grid > .pf-m-offset-7-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(7 + 1); }\n .pf-l-grid > .pf-m-offset-8-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(8 + 1); }\n .pf-l-grid > .pf-m-offset-9-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(9 + 1); }\n .pf-l-grid > .pf-m-offset-10-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(10 + 1); }\n .pf-l-grid > .pf-m-offset-11-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(11 + 1); }\n .pf-l-grid > .pf-m-offset-12-col-on-2xl {\n --pf-l-grid__item--GridColumnStart: col-start calc(12 + 1); }\n .pf-l-grid > .pf-m-1-row-on-2xl {\n grid-row: span 1; }\n .pf-l-grid > .pf-m-2-row-on-2xl {\n grid-row: span 2; }\n .pf-l-grid > .pf-m-3-row-on-2xl {\n grid-row: span 3; }\n .pf-l-grid > .pf-m-4-row-on-2xl {\n grid-row: span 4; }\n .pf-l-grid > .pf-m-5-row-on-2xl {\n grid-row: span 5; }\n .pf-l-grid > .pf-m-6-row-on-2xl {\n grid-row: span 6; }\n .pf-l-grid > .pf-m-7-row-on-2xl {\n grid-row: span 7; }\n .pf-l-grid > .pf-m-8-row-on-2xl {\n grid-row: span 8; }\n .pf-l-grid > .pf-m-9-row-on-2xl {\n grid-row: span 9; }\n .pf-l-grid > .pf-m-10-row-on-2xl {\n grid-row: span 10; }\n .pf-l-grid > .pf-m-11-row-on-2xl {\n grid-row: span 11; }\n .pf-l-grid > .pf-m-12-row-on-2xl {\n grid-row: span 12; } }\n .pf-l-grid.pf-m-gutter {\n grid-gap: var(--pf-l-grid--m-gutter--GridGap); }\n\n.pf-l-level {\n --pf-l-level--m-gutter--MarginRight: var(--pf-global--gutter);\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between; }\n .pf-l-level.pf-m-gutter > *:not(:last-child) {\n margin-right: var(--pf-l-level--m-gutter--MarginRight); }\n\n.pf-l-split {\n --pf-l-split--m-gutter--MarginRight: var(--pf-global--gutter);\n display: flex;\n flex-wrap: nowrap;\n padding: 0;\n margin: 0; }\n\n.pf-l-split__item.pf-m-fill {\n flex-grow: 1; }\n\n.pf-l-split.pf-m-gutter > *:not(:last-child) {\n margin-right: var(--pf-l-split--m-gutter--MarginRight); }\n\n.pf-l-stack {\n --pf-l-stack--m-gutter--MarginBottom: var(--pf-global--gutter);\n display: flex;\n flex-direction: column;\n height: 100%; }\n\n.pf-l-stack__item.pf-m-fill {\n flex-grow: 1; }\n\n.pf-l-stack.pf-m-gutter > *:not(:last-child) {\n margin-bottom: var(--pf-l-stack--m-gutter--MarginBottom); }\n"]} \ No newline at end of file diff --git a/awx/ui/public/static/js/d3-collection.v1.min.js b/awx/ui/public/static/js/d3-collection.v1.min.js deleted file mode 100644 index bffa8f534e0f..000000000000 --- a/awx/ui/public/static/js/d3-collection.v1.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable */ -// https://d3js.org/d3-collection/ v1.0.7 Copyright 2018 Mike Bostock -!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.d3=n.d3||{})}(this,function(n){"use strict";function t(){}function e(n,e){var r=new t;if(n instanceof t)n.each(function(n,t){r.set(t,n)});else if(Array.isArray(n)){var i,u=-1,o=n.length;if(null==e)for(;++u=f.length)return null!=n&&r.sort(n),null!=t?t(r):r;for(var s,c,h,l=-1,v=r.length,p=f[i++],y=e(),d=u();++lf.length)return e;var i,u=c[r-1];return null!=t&&r>=f.length?i=e.entries():(i=[],e.each(function(t,e){i.push({key:e,values:n(t,r)})})),null!=u?i.sort(function(n,t){return u(n.key,t.key)}):i}(a(n,0,u,o),0)},key:function(n){return f.push(n),s},sortKeys:function(n){return c[f.length-1]=n,s},sortValues:function(t){return n=t,s},rollup:function(n){return t=n,s}}},n.set=c,n.map=e,n.keys=function(n){var t=[];for(var e in n)t.push(e);return t},n.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},n.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Object.defineProperty(n,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/awx/ui/public/static/js/d3-dispatch.v1.min.js b/awx/ui/public/static/js/d3-dispatch.v1.min.js deleted file mode 100644 index 818ea65001be..000000000000 --- a/awx/ui/public/static/js/d3-dispatch.v1.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint-disable */ -// https://d3js.org/d3-dispatch/ v1.0.6 Copyright 2019 Mike Bostock -!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((n=n||self).d3=n.d3||{})}(this,function(n){"use strict";var e={value:function(){}};function t(){for(var n,e=0,t=arguments.length,o={};e=0&&(t=n.slice(r+1),n=n.slice(0,r)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:t}})}function i(n,e){for(var t,r=0,o=n.length;r0)for(var t,r,o=new Array(t),i=0;iv+c||ry+c||ul.index){var h=v-f.x-f.vx,g=y-f.y-f.vy,s=h*h+g*g;sn.r&&(n.r=n[t].r)}function v(){if(e){var t,i,u=e.length;for(r=new Array(u),t=0;t=c)){(n.data!==e||n.next)&&(0===h&&(d+=(h=o())*h),0===v&&(d+=(v=o())*v),d1?(null==e?l.remove(n):l.set(n,p(e)),t):l.get(n)},find:function(t,e,r){var i,u,o,f,a,c=0,l=n.length;for(null==r?r=1/0:r*=r,c=0;c1?(v.on(n,e),t):v.on(n)}}},n.forceX=function(n){var t,e,r,i=u(.1);function o(n){for(var i,u=0,o=t.length;u=(s=(y+v)/2))?y=s:v=s,(l=r>=(h=(d+p)/2))?d=h:p=h,n=c,!(c=c[_=l<<1|u]))return n[_]=x,t;if(o=+t._x.call(null,c.data),a=+t._y.call(null,c.data),i===o&&r===a)return x.next=c,n?n[_]=x:t._root=x,t;do{n=n?n[_]=new Array(4):t._root=new Array(4),(u=i>=(s=(y+v)/2))?y=s:v=s,(l=r>=(h=(d+p)/2))?d=h:p=h}while((_=l<<1|u)==(f=(a>=h)<<1|o>=s));return n[f]=c,n[_]=x,t}function r(t,i,r,e,n){this.node=t,this.x0=i,this.y0=r,this.x1=e,this.y1=n}function e(t){return t[0]}function n(t){return t[1]}function s(t,i,r){var s=new h(null==i?e:i,null==r?n:r,NaN,NaN,NaN,NaN);return null==t?s:s.addAll(t)}function h(t,i,r,e,n,s){this._x=t,this._y=i,this._x0=r,this._y0=e,this._x1=n,this._y1=s,this._root=void 0}function o(t){for(var i={data:t.data},r=i;t=t.next;)r=r.next={data:t.data};return i}var a=s.prototype=h.prototype;a.copy=function(){var t,i,r=new h(this._x,this._y,this._x0,this._y0,this._x1,this._y1),e=this._root;if(!e)return r;if(!e.length)return r._root=o(e),r;for(t=[{source:e,target:r._root=new Array(4)}];e=t.pop();)for(var n=0;n<4;++n)(i=e.source[n])&&(i.length?t.push({source:i,target:e.target[n]=new Array(4)}):e.target[n]=o(i));return r},a.add=function(t){var r=+this._x.call(null,t),e=+this._y.call(null,t);return i(this.cover(r,e),r,e,t)},a.addAll=function(t){var r,e,n,s,h=t.length,o=new Array(h),a=new Array(h),u=1/0,l=1/0,_=-1/0,f=-1/0;for(e=0;e_&&(_=n),sf&&(f=s));if(u>_||l>f)return this;for(this.cover(u,l).cover(_,f),e=0;et||t>=n||e>i||i>=s;)switch(o=(ic||(h=u.y0)>x||(o=u.x1)<_||(a=u.y1)=p)<<1|t>=v)&&(u=y[y.length-1],y[y.length-1]=y[y.length-1-l],y[y.length-1-l]=u)}else{var w=t-+this._x.call(null,d.data),N=i-+this._y.call(null,d.data),g=w*w+N*N;if(g=(o=(x+d)/2))?x=o:d=o,(l=h>=(a=(y+v)/2))?y=a:v=a,i=c,!(c=c[_=l<<1|u]))return this;if(!c.length)break;(i[_+1&3]||i[_+2&3]||i[_+3&3])&&(r=i,f=_)}for(;c.data!==t;)if(e=c,!(c=c.next))return this;return(n=c.next)&&delete c.next,e?(n?e.next=n:delete e.next,this):i?(n?i[_]=n:delete i[_],(c=i[0]||i[1]||i[2]||i[3])&&c===(i[3]||i[2]||i[1]||i[0])&&!c.length&&(r?r[f]=c:this._root=c),this):(this._root=n,this)},a.removeAll=function(t){for(var i=0,r=t.length;i=0&&e._call.call(null,t),e=e._next;--o}function h(){c=(l=a.now())+f,o=i=0;try{d()}finally{o=0,function(){var t,o,i=n,r=1/0;for(;i;)i._call?(r>i._time&&(r=i._time),t=i,i=i._next):(o=i._next,i._next=null,i=t?t._next=o:n=o);e=t,v(r)}(),c=0}}function y(){var t=a.now(),n=t-l;n>u&&(f-=n,l=t)}function v(t){o||(i&&(i=clearTimeout(i)),t-c>24?(t<1/0&&(i=setTimeout(h,t-a.now()-f)),r&&(r=clearInterval(r))):(r||(l=a.now(),r=setInterval(y,u)),o=1,s(h)))}p.prototype=w.prototype={constructor:p,restart:function(t,o,i){if("function"!=typeof t)throw new TypeError("callback is not a function");i=(null==i?_():+i)+(null==o?0:+o),this._next||e===this||(e?e._next=this:n=this,e=this),this._call=t,this._time=i,v()},stop:function(){this._call&&(this._call=null,this._time=1/0,v())}},t.interval=function(t,n,e){var o=new p,i=n;return null==n?(o.restart(t,n,e),o):(n=+n,e=null==e?_():+e,o.restart(function r(u){u+=i,o.restart(r,i+=n,e),t(u)},n,e),o)},t.now=_,t.timeout=function(t,n,e){var o=new p;return n=null==n?0:+n,o.restart(function(e){o.stop(),t(e+n)},n,e),o},t.timer=w,t.timerFlush=d,Object.defineProperty(t,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/awx/ui/public/static/media/192.png b/awx/ui/public/static/media/192.png new file mode 100644 index 000000000000..dd37dd9c035f Binary files /dev/null and b/awx/ui/public/static/media/192.png differ diff --git a/awx/ui/public/static/media/256.png b/awx/ui/public/static/media/256.png new file mode 100644 index 000000000000..e5df858b89bf Binary files /dev/null and b/awx/ui/public/static/media/256.png differ diff --git a/awx/ui/public/static/media/384.png b/awx/ui/public/static/media/384.png new file mode 100644 index 000000000000..c5c9f66a8eaf Binary files /dev/null and b/awx/ui/public/static/media/384.png differ diff --git a/awx/ui/public/static/media/512.png b/awx/ui/public/static/media/512.png new file mode 100644 index 000000000000..a53944ac70d5 Binary files /dev/null and b/awx/ui/public/static/media/512.png differ diff --git a/awx/ui/public/static/media/brand-logo.png b/awx/ui/public/static/media/brand-logo.png new file mode 100644 index 000000000000..6a52016f04a1 Binary files /dev/null and b/awx/ui/public/static/media/brand-logo.png differ diff --git a/awx/ui/public/static/media/brand-logo.svg b/awx/ui/public/static/media/brand-logo.svg new file mode 100644 index 000000000000..6d80915fb665 --- /dev/null +++ b/awx/ui/public/static/media/brand-logo.svg @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/ui/public/static/media/brand-logo192.png b/awx/ui/public/static/media/brand-logo192.png new file mode 100644 index 000000000000..74489717d03f Binary files /dev/null and b/awx/ui/public/static/media/brand-logo192.png differ diff --git a/awx/ui/public/static/media/favicon.png b/awx/ui/public/static/media/favicon.png new file mode 100644 index 000000000000..170ed20f8924 Binary files /dev/null and b/awx/ui/public/static/media/favicon.png differ diff --git a/awx/ui/public/static/media/favicon.svg b/awx/ui/public/static/media/favicon.svg new file mode 100644 index 000000000000..6d80915fb665 --- /dev/null +++ b/awx/ui/public/static/media/favicon.svg @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/awx/ui/src/App.js b/awx/ui/src/App.js deleted file mode 100644 index 5f35ecfbc2d7..000000000000 --- a/awx/ui/src/App.js +++ /dev/null @@ -1,192 +0,0 @@ -import React, { useEffect } from 'react'; -import { - useRouteMatch, - useLocation, - HashRouter, - Route, - Switch, - Redirect, - useHistory, -} from 'react-router-dom'; -import { ErrorBoundary } from 'react-error-boundary'; -import { I18nProvider } from '@lingui/react'; -import { i18n } from '@lingui/core'; -import { Card, PageSection } from '@patternfly/react-core'; -import { - ConfigProvider, - useAuthorizedPath, - useUserProfile, -} from 'contexts/Config'; -import { SessionProvider, useSession } from 'contexts/Session'; -import AppContainer from 'components/AppContainer'; -import Background from 'components/Background'; -import ContentError from 'components/ContentError'; -import NotFound from 'screens/NotFound'; -import Login from 'screens/Login'; -import { isAuthenticated } from 'util/auth'; -import { getLanguageWithoutRegionCode } from 'util/language'; -import Metrics from 'screens/Metrics'; -import SubscriptionEdit from 'screens/Setting/Subscription/SubscriptionEdit'; -import useTitle from 'hooks/useTitle'; -import { dynamicActivate, locales } from './i18nLoader'; -import getRouteConfig from './routeConfig'; -import { SESSION_REDIRECT_URL } from './constants'; - -function ErrorFallback({ error }) { - return ( - - - - - - ); -} - -const RenderAppContainer = () => { - const userProfile = useUserProfile(); - const navRouteConfig = getRouteConfig(userProfile); - - return ( - - - - ); -}; - -const AuthorizedRoutes = ({ routeConfig }) => { - const isAuthorized = useAuthorizedPath(); - const match = useRouteMatch(); - - if (!isAuthorized) { - return ( - - - - - - - - - - - - - ); - } - - return ( - - {routeConfig - .flatMap(({ routes }) => routes) - .map(({ path, screen: Screen }) => ( - - - - )) - .concat( - - - , - - - - )} - - ); -}; - -export function ProtectedRoute({ children, ...rest }) { - const { - authRedirectTo, - isUserBeingLoggedOut, - loginRedirectOverride, - setAuthRedirectTo, - } = useSession(); - const location = useLocation(); - - useEffect(() => { - setAuthRedirectTo( - authRedirectTo === '/logout' - ? '/' - : `${location.pathname}${location.search}` - ); - }); - - if (isAuthenticated(document.cookie)) { - return ( - - - {children} - - - ); - } - - if ( - loginRedirectOverride && - !window.location.href.includes('/login') && - !isUserBeingLoggedOut - ) { - window.location.replace(loginRedirectOverride); - return null; - } - return ; -} - -function App() { - const history = useHistory(); - const { hash, search, pathname } = useLocation(); - let language = getLanguageWithoutRegionCode(navigator); - if (!Object.keys(locales).includes(language)) { - // If there isn't a string catalog available for the browser's - // preferred language, default to one that has strings. - language = 'en'; - } - - useEffect(() => { - dynamicActivate(language); - }, [language]); - - useTitle(); - - const redirectURL = window.sessionStorage.getItem(SESSION_REDIRECT_URL); - if (redirectURL) { - window.sessionStorage.removeItem(SESSION_REDIRECT_URL); - if (redirectURL !== '/' || redirectURL !== '/home') - history.replace(redirectURL); - } - - return ( - - - - - - - - - - - - - - - - - - - - - - - ); -} - -export default () => ( - - - -); diff --git a/awx/ui/src/App.test.js b/awx/ui/src/App.test.js deleted file mode 100644 index e1f2fb3bc342..000000000000 --- a/awx/ui/src/App.test.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { RootAPI } from 'api'; -import * as SessionContext from 'contexts/Session'; -import { shallow } from 'enzyme'; -import { mountWithContexts } from '../testUtils/enzymeHelpers'; -import App, { ProtectedRoute } from './App'; - -jest.mock('./api'); -jest.mock('util/webWorker', () => jest.fn()); - -describe('', () => { - beforeEach(() => { - RootAPI.readAssetVariables.mockResolvedValue({ - data: { - BRAND_NAME: 'AWX', - }, - }); - }); - - test('renders ok', async () => { - const contextValues = { - setAuthRedirectTo: jest.fn(), - isSessionExpired: false, - isUserBeingLoggedOut: false, - loginRedirectOverride: null, - }; - jest - .spyOn(SessionContext, 'useSession') - .mockImplementation(() => contextValues); - - let wrapper; - await act(async () => { - wrapper = shallow(); - }); - expect(wrapper.length).toBe(1); - jest.clearAllMocks(); - }); - - test('redirect to login override', async () => { - const { location } = window; - delete window.location; - window.location = { - replace: jest.fn(), - href: '/', - }; - - expect(window.location.replace).not.toHaveBeenCalled(); - - const contextValues = { - setAuthRedirectTo: jest.fn(), - isSessionExpired: false, - isUserBeingLoggedOut: false, - loginRedirectOverride: '/sso/test', - }; - jest - .spyOn(SessionContext, 'useSession') - .mockImplementation(() => contextValues); - - await act(async () => { - mountWithContexts( - -
foo
-
- ); - }); - - expect(window.location.replace).toHaveBeenCalled(); - window.location = location; - }); -}); diff --git a/awx/ui/src/api/Base.js b/awx/ui/src/api/Base.js deleted file mode 100644 index d51669d4d30f..000000000000 --- a/awx/ui/src/api/Base.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable default-param-last */ -import axios from 'axios'; -import { encodeQueryString } from 'util/qs'; -import debounce from 'util/debounce'; -import { SESSION_TIMEOUT_KEY } from '../constants'; - -const updateStorage = debounce((key, val) => { - window.localStorage.setItem(key, val); - window.dispatchEvent(new Event('storage')); -}, 500); - -const defaultHttp = axios.create({ - xsrfCookieName: 'csrftoken', - xsrfHeaderName: 'X-CSRFToken', - paramsSerializer(params) { - return encodeQueryString(params); - }, -}); - -defaultHttp.interceptors.response.use((response) => { - const timeout = response?.headers['session-timeout']; - if (timeout) { - const timeoutDate = new Date().getTime() + timeout * 1000; - updateStorage(SESSION_TIMEOUT_KEY, String(timeoutDate)); - } - return response; -}); - -class Base { - constructor(http = defaultHttp, baseURL) { - this.http = http; - this.baseUrl = baseURL; - } - - create(data) { - return this.http.post(this.baseUrl, data); - } - - destroy(id) { - return this.http.delete(`${this.baseUrl}${id}/`); - } - - read(params) { - return this.http.get(this.baseUrl, { - params, - }); - } - - readDetail(id) { - return this.http.get(`${this.baseUrl}${id}/`); - } - - readOptions() { - return this.http.options(this.baseUrl); - } - - replace(id, data) { - return this.http.put(`${this.baseUrl}${id}/`, data); - } - - update(id, data) { - return this.http.patch(`${this.baseUrl}${id}/`, data); - } - - copy(id, data) { - return this.http.post(`${this.baseUrl}${id}/copy/`, data); - } -} - -export default Base; diff --git a/awx/ui/src/api/Base.test.js b/awx/ui/src/api/Base.test.js deleted file mode 100644 index 7a8f1621508e..000000000000 --- a/awx/ui/src/api/Base.test.js +++ /dev/null @@ -1,106 +0,0 @@ -import Base from './Base'; - -describe('Base', () => { - const mockBaseURL = '/api/v2/organizations/'; - - let BaseAPI; - let mockHttp; - - beforeEach(() => { - const createPromise = () => Promise.resolve(); - mockHttp = { - delete: jest.fn(createPromise), - get: jest.fn(createPromise), - options: jest.fn(createPromise), - patch: jest.fn(createPromise), - post: jest.fn(createPromise), - put: jest.fn(createPromise), - }; - BaseAPI = new Base(mockHttp, mockBaseURL); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - test('create calls http method with expected data', async () => { - const data = { name: 'test ' }; - await BaseAPI.create(data); - - expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0][1]).toEqual(data); - }); - - test('destroy calls http method with expected data', async () => { - const resourceId = 1; - await BaseAPI.destroy(resourceId); - - expect(mockHttp.delete).toHaveBeenCalledTimes(1); - expect(mockHttp.delete.mock.calls[0][0]).toEqual( - `${mockBaseURL}${resourceId}/` - ); - }); - - test('read calls http method with expected data', async () => { - const testParams = { foo: 'bar' }; - const testParamsDuplicates = { foo: ['bar', 'baz'] }; - - await BaseAPI.read(testParams); - await BaseAPI.read(); - await BaseAPI.read(testParamsDuplicates); - - expect(mockHttp.get).toHaveBeenCalledTimes(3); - expect(mockHttp.get.mock.calls[0][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[0][1]).toEqual({ params: { foo: 'bar' } }); - expect(mockHttp.get.mock.calls[1][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[1][1]).toEqual({ params: undefined }); - expect(mockHttp.get.mock.calls[2][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[2][1]).toEqual({ - params: { foo: ['bar', 'baz'] }, - }); - }); - - test('readDetail calls http method with expected data', async () => { - const resourceId = 1; - - await BaseAPI.readDetail(resourceId); - - expect(mockHttp.get).toHaveBeenCalledTimes(1); - expect(mockHttp.get.mock.calls[0][0]).toEqual( - `${mockBaseURL}${resourceId}/` - ); - }); - - test('readOptions calls http method with expected data', async () => { - await BaseAPI.readOptions(); - - expect(mockHttp.options).toHaveBeenCalledTimes(1); - expect(mockHttp.options.mock.calls[0][0]).toEqual(`${mockBaseURL}`); - }); - - test('replace calls http method with expected data', async () => { - const resourceId = 1; - const data = { name: 'test ' }; - - await BaseAPI.replace(resourceId, data); - - expect(mockHttp.put).toHaveBeenCalledTimes(1); - expect(mockHttp.put.mock.calls[0][0]).toEqual( - `${mockBaseURL}${resourceId}/` - ); - expect(mockHttp.put.mock.calls[0][1]).toEqual(data); - }); - - test('update calls http method with expected data', async () => { - const resourceId = 1; - const data = { name: 'test ' }; - - await BaseAPI.update(resourceId, data); - - expect(mockHttp.patch).toHaveBeenCalledTimes(1); - expect(mockHttp.patch.mock.calls[0][0]).toEqual( - `${mockBaseURL}${resourceId}/` - ); - expect(mockHttp.patch.mock.calls[0][1]).toEqual(data); - }); -}); diff --git a/awx/ui/src/api/index.js b/awx/ui/src/api/index.js deleted file mode 100644 index 5281ad861d10..000000000000 --- a/awx/ui/src/api/index.js +++ /dev/null @@ -1,142 +0,0 @@ -import ActivityStream from './models/ActivityStream'; -import AdHocCommands from './models/AdHocCommands'; -import Applications from './models/Applications'; -import Auth from './models/Auth'; -import Config from './models/Config'; -import CredentialInputSources from './models/CredentialInputSources'; -import CredentialTypes from './models/CredentialTypes'; -import Credentials from './models/Credentials'; -import Dashboard from './models/Dashboard'; -import ExecutionEnvironments from './models/ExecutionEnvironments'; -import Groups from './models/Groups'; -import Hosts from './models/Hosts'; -import InstanceGroups from './models/InstanceGroups'; -import Instances from './models/Instances'; -import Inventories from './models/Inventories'; -import InventoryScripts from './models/InventoryScripts'; -import InventorySources from './models/InventorySources'; -import InventoryUpdates from './models/InventoryUpdates'; -import JobTemplates from './models/JobTemplates'; -import Jobs from './models/Jobs'; -import JobEvents from './models/JobEvents'; -import Labels from './models/Labels'; -import Me from './models/Me'; -import Mesh from './models/Mesh'; -import Metrics from './models/Metrics'; -import NotificationTemplates from './models/NotificationTemplates'; -import Notifications from './models/Notifications'; -import Organizations from './models/Organizations'; -import ProjectUpdates from './models/ProjectUpdates'; -import Projects from './models/Projects'; -import Roles from './models/Roles'; -import Root from './models/Root'; -import Schedules from './models/Schedules'; -import Settings from './models/Settings'; -import SystemJobs from './models/SystemJobs'; -import SystemJobTemplates from './models/SystemJobTemplates'; -import Teams from './models/Teams'; -import Tokens from './models/Tokens'; -import UnifiedJobTemplates from './models/UnifiedJobTemplates'; -import UnifiedJobs from './models/UnifiedJobs'; -import Users from './models/Users'; -import WorkflowApprovals from './models/WorkflowApprovals'; -import WorkflowApprovalTemplates from './models/WorkflowApprovalTemplates'; -import WorkflowJobTemplateNodes from './models/WorkflowJobTemplateNodes'; -import WorkflowJobTemplates from './models/WorkflowJobTemplates'; -import WorkflowJobs from './models/WorkflowJobs'; - -const ActivityStreamAPI = new ActivityStream(); -const AdHocCommandsAPI = new AdHocCommands(); -const ApplicationsAPI = new Applications(); -const AuthAPI = new Auth(); -const ConfigAPI = new Config(); -const CredentialInputSourcesAPI = new CredentialInputSources(); -const CredentialTypesAPI = new CredentialTypes(); -const CredentialsAPI = new Credentials(); -const DashboardAPI = new Dashboard(); -const ExecutionEnvironmentsAPI = new ExecutionEnvironments(); -const GroupsAPI = new Groups(); -const HostsAPI = new Hosts(); -const InstanceGroupsAPI = new InstanceGroups(); -const InstancesAPI = new Instances(); -const InventoriesAPI = new Inventories(); -const InventoryScriptsAPI = new InventoryScripts(); -const InventorySourcesAPI = new InventorySources(); -const InventoryUpdatesAPI = new InventoryUpdates(); -const JobTemplatesAPI = new JobTemplates(); -const JobsAPI = new Jobs(); -const JobEventsAPI = new JobEvents(); -const LabelsAPI = new Labels(); -const MeAPI = new Me(); -const MeshAPI = new Mesh(); -const MetricsAPI = new Metrics(); -const NotificationTemplatesAPI = new NotificationTemplates(); -const NotificationsAPI = new Notifications(); -const OrganizationsAPI = new Organizations(); -const ProjectUpdatesAPI = new ProjectUpdates(); -const ProjectsAPI = new Projects(); -const RolesAPI = new Roles(); -const RootAPI = new Root(); -const SchedulesAPI = new Schedules(); -const SettingsAPI = new Settings(); -const SystemJobsAPI = new SystemJobs(); -const SystemJobTemplatesAPI = new SystemJobTemplates(); -const TeamsAPI = new Teams(); -const TokensAPI = new Tokens(); -const UnifiedJobTemplatesAPI = new UnifiedJobTemplates(); -const UnifiedJobsAPI = new UnifiedJobs(); -const UsersAPI = new Users(); -const WorkflowApprovalsAPI = new WorkflowApprovals(); -const WorkflowApprovalTemplatesAPI = new WorkflowApprovalTemplates(); -const WorkflowJobTemplateNodesAPI = new WorkflowJobTemplateNodes(); -const WorkflowJobTemplatesAPI = new WorkflowJobTemplates(); -const WorkflowJobsAPI = new WorkflowJobs(); - -export { - ActivityStreamAPI, - AdHocCommandsAPI, - ApplicationsAPI, - AuthAPI, - ConfigAPI, - CredentialInputSourcesAPI, - CredentialTypesAPI, - CredentialsAPI, - DashboardAPI, - ExecutionEnvironmentsAPI, - GroupsAPI, - HostsAPI, - InstanceGroupsAPI, - InstancesAPI, - InventoriesAPI, - InventoryScriptsAPI, - InventorySourcesAPI, - InventoryUpdatesAPI, - JobTemplatesAPI, - JobsAPI, - JobEventsAPI, - LabelsAPI, - MeAPI, - MeshAPI, - MetricsAPI, - NotificationTemplatesAPI, - NotificationsAPI, - OrganizationsAPI, - ProjectUpdatesAPI, - ProjectsAPI, - RolesAPI, - RootAPI, - SchedulesAPI, - SettingsAPI, - SystemJobsAPI, - SystemJobTemplatesAPI, - TeamsAPI, - TokensAPI, - UnifiedJobTemplatesAPI, - UnifiedJobsAPI, - UsersAPI, - WorkflowApprovalsAPI, - WorkflowApprovalTemplatesAPI, - WorkflowJobTemplateNodesAPI, - WorkflowJobTemplatesAPI, - WorkflowJobsAPI, -}; diff --git a/awx/ui/src/api/mixins/InstanceGroups.mixin.js b/awx/ui/src/api/mixins/InstanceGroups.mixin.js deleted file mode 100644 index e2e230122c8c..000000000000 --- a/awx/ui/src/api/mixins/InstanceGroups.mixin.js +++ /dev/null @@ -1,44 +0,0 @@ -function isEqual(array1, array2) { - return ( - array1.length === array2.length && - array1.every((element, index) => element.id === array2[index].id) - ); -} - -const InstanceGroupsMixin = (parent) => - class extends parent { - readInstanceGroups(resourceId, params) { - return this.http.get(`${this.baseUrl}${resourceId}/instance_groups/`, { - params, - }); - } - - associateInstanceGroup(resourceId, instanceGroupId) { - return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { - id: instanceGroupId, - }); - } - - disassociateInstanceGroup(resourceId, instanceGroupId) { - return this.http.post(`${this.baseUrl}${resourceId}/instance_groups/`, { - id: instanceGroupId, - disassociate: true, - }); - } - - async orderInstanceGroups(resourceId, current, original) { - /* eslint-disable no-await-in-loop, no-restricted-syntax */ - // Resolve Promises sequentially to maintain order and avoid race condition - if (!isEqual(current, original)) { - for (const group of original) { - await this.disassociateInstanceGroup(resourceId, group.id); - } - for (const group of current) { - await this.associateInstanceGroup(resourceId, group.id); - } - } - } - /* eslint-enable no-await-in-loop, no-restricted-syntax */ - }; - -export default InstanceGroupsMixin; diff --git a/awx/ui/src/api/mixins/Labels.mixin.js b/awx/ui/src/api/mixins/Labels.mixin.js deleted file mode 100644 index 98aae12034cb..000000000000 --- a/awx/ui/src/api/mixins/Labels.mixin.js +++ /dev/null @@ -1,49 +0,0 @@ -const LabelsMixin = (parent) => - class extends parent { - readLabels(id, params) { - return this.http.get(`${this.baseUrl}${id}/labels/`, { - params, - }); - } - - readAllLabels(id) { - const fetchLabels = async (pageNo = 1, labels = []) => { - try { - const { data } = await this.http.get(`${this.baseUrl}${id}/labels/`, { - params: { - page: pageNo, - page_size: 200, - }, - }); - if (data?.next) { - return fetchLabels(pageNo + 1, labels.concat(data.results)); - } - return Promise.resolve({ - data: { - results: labels.concat(data.results), - }, - }); - } catch (error) { - return Promise.reject(error); - } - }; - - return fetchLabels(); - } - - associateLabel(id, label, orgId) { - return this.http.post(`${this.baseUrl}${id}/labels/`, { - name: label.name, - organization: orgId, - }); - } - - disassociateLabel(id, label) { - return this.http.post(`${this.baseUrl}${id}/labels/`, { - id: label.id, - disassociate: true, - }); - } - }; - -export default LabelsMixin; diff --git a/awx/ui/src/api/mixins/LaunchUpdate.mixin.js b/awx/ui/src/api/mixins/LaunchUpdate.mixin.js deleted file mode 100644 index cad3c0e53f08..000000000000 --- a/awx/ui/src/api/mixins/LaunchUpdate.mixin.js +++ /dev/null @@ -1,12 +0,0 @@ -const LaunchUpdateMixin = (parent) => - class extends parent { - launchUpdate(id, data) { - return this.http.post(`${this.baseUrl}${id}/update/`, data); - } - - readLaunchUpdate(id) { - return this.http.get(`${this.baseUrl}${id}/update/`); - } - }; - -export default LaunchUpdateMixin; diff --git a/awx/ui/src/api/mixins/Notifications.mixin.js b/awx/ui/src/api/mixins/Notifications.mixin.js deleted file mode 100644 index c3782e2d8eae..000000000000 --- a/awx/ui/src/api/mixins/Notifications.mixin.js +++ /dev/null @@ -1,170 +0,0 @@ -const NotificationsMixin = (parent) => - class extends parent { - readOptionsNotificationTemplates(id) { - return this.http.options(`${this.baseUrl}${id}/notification_templates/`); - } - - readNotificationTemplates(id, params) { - return this.http.get( - `${this.baseUrl}${id}/notification_templates/`, - params - ); - } - - readNotificationTemplatesStarted(id, params) { - return this.http.get( - `${this.baseUrl}${id}/notification_templates_started/`, - { params } - ); - } - - readNotificationTemplatesSuccess(id, params) { - return this.http.get( - `${this.baseUrl}${id}/notification_templates_success/`, - { params } - ); - } - - readNotificationTemplatesError(id, params) { - return this.http.get( - `${this.baseUrl}${id}/notification_templates_error/`, - { params } - ); - } - - associateNotificationTemplatesStarted(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_started/`, - { id: notificationId } - ); - } - - disassociateNotificationTemplatesStarted(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_started/`, - { id: notificationId, disassociate: true } - ); - } - - associateNotificationTemplatesSuccess(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_success/`, - { id: notificationId } - ); - } - - disassociateNotificationTemplatesSuccess(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_success/`, - { id: notificationId, disassociate: true } - ); - } - - associateNotificationTemplatesError(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_error/`, - { id: notificationId } - ); - } - - disassociateNotificationTemplatesError(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_error/`, - { id: notificationId, disassociate: true } - ); - } - - /** - * This is a helper method meant to simplify setting the "on" status of - * a related notification. - * - * @param[resourceId] - id of the base resource - * @param[notificationId] - id of the notification - * @param[notificationType] - the type of notification, options are "success" and "error" - */ - associateNotificationTemplate( - resourceId, - notificationId, - notificationType - ) { - if (notificationType === 'approvals') { - return this.associateNotificationTemplatesApprovals( - resourceId, - notificationId - ); - } - - if (notificationType === 'started') { - return this.associateNotificationTemplatesStarted( - resourceId, - notificationId - ); - } - - if (notificationType === 'success') { - return this.associateNotificationTemplatesSuccess( - resourceId, - notificationId - ); - } - - if (notificationType === 'error') { - return this.associateNotificationTemplatesError( - resourceId, - notificationId - ); - } - - throw new Error( - `Unsupported notificationType for association: ${notificationType}` - ); - } - - /** - * This is a helper method meant to simplify setting the "off" status of - * a related notification. - * - * @param[resourceId] - id of the base resource - * @param[notificationId] - id of the notification - * @param[notificationType] - the type of notification, options are "success" and "error" - */ - disassociateNotificationTemplate( - resourceId, - notificationId, - notificationType - ) { - if (notificationType === 'approvals') { - return this.disassociateNotificationTemplatesApprovals( - resourceId, - notificationId - ); - } - - if (notificationType === 'started') { - return this.disassociateNotificationTemplatesStarted( - resourceId, - notificationId - ); - } - - if (notificationType === 'success') { - return this.disassociateNotificationTemplatesSuccess( - resourceId, - notificationId - ); - } - - if (notificationType === 'error') { - return this.disassociateNotificationTemplatesError( - resourceId, - notificationId - ); - } - - throw new Error( - `Unsupported notificationType for disassociation: ${notificationType}` - ); - } - }; - -export default NotificationsMixin; diff --git a/awx/ui/src/api/mixins/Runnable.mixin.js b/awx/ui/src/api/mixins/Runnable.mixin.js deleted file mode 100644 index 2c9b27b28818..000000000000 --- a/awx/ui/src/api/mixins/Runnable.mixin.js +++ /dev/null @@ -1,48 +0,0 @@ -const Runnable = (parent) => - class extends parent { - jobEventSlug = '/events/'; - - cancel(id) { - const endpoint = `${this.baseUrl}${id}/cancel/`; - - return this.http.post(endpoint); - } - - launchUpdate(id, data) { - const endpoint = `${this.baseUrl}${id}/update/`; - - return this.http.post(endpoint, data); - } - - readLaunchUpdate(id) { - const endpoint = `${this.baseUrl}${id}/update/`; - - return this.http.get(endpoint); - } - - readEvents(id, params = {}) { - const endpoint = `${this.baseUrl}${id}${this.jobEventSlug}`; - - return this.http.get(endpoint, { params }); - } - - readEventOptions(id) { - const endpoint = `${this.baseUrl}${id}${this.jobEventSlug}`; - - return this.http.options(endpoint); - } - - readRelaunch(id) { - const endpoint = `${this.baseUrl}${id}/relaunch/`; - - return this.http.get(endpoint); - } - - relaunch(id, data) { - const endpoint = `${this.baseUrl}${id}/relaunch/`; - - return this.http.post(endpoint, data); - } - }; - -export default Runnable; diff --git a/awx/ui/src/api/mixins/Schedules.mixin.js b/awx/ui/src/api/mixins/Schedules.mixin.js deleted file mode 100644 index 450e7c727a40..000000000000 --- a/awx/ui/src/api/mixins/Schedules.mixin.js +++ /dev/null @@ -1,16 +0,0 @@ -const SchedulesMixin = (parent) => - class extends parent { - createSchedule(id, data) { - return this.http.post(`${this.baseUrl}${id}/schedules/`, data); - } - - readSchedules(id, params) { - return this.http.get(`${this.baseUrl}${id}/schedules/`, { params }); - } - - readScheduleOptions(id) { - return this.http.options(`${this.baseUrl}${id}/schedules/`); - } - }; - -export default SchedulesMixin; diff --git a/awx/ui/src/api/models/ActivityStream.js b/awx/ui/src/api/models/ActivityStream.js deleted file mode 100644 index 68f748dc18bd..000000000000 --- a/awx/ui/src/api/models/ActivityStream.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class ActivityStream extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/activity_stream/'; - } -} - -export default ActivityStream; diff --git a/awx/ui/src/api/models/AdHocCommands.js b/awx/ui/src/api/models/AdHocCommands.js deleted file mode 100644 index 0bec670de9ae..000000000000 --- a/awx/ui/src/api/models/AdHocCommands.js +++ /dev/null @@ -1,15 +0,0 @@ -import Base from '../Base'; -import RunnableMixin from '../mixins/Runnable.mixin'; - -class AdHocCommands extends RunnableMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/ad_hoc_commands/'; - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } -} - -export default AdHocCommands; diff --git a/awx/ui/src/api/models/Applications.js b/awx/ui/src/api/models/Applications.js deleted file mode 100644 index d53f911dbc02..000000000000 --- a/awx/ui/src/api/models/Applications.js +++ /dev/null @@ -1,20 +0,0 @@ -import Base from '../Base'; - -class Applications extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/applications/'; - } - - readTokens(appId, params) { - return this.http.get(`${this.baseUrl}${appId}/tokens/`, { - params, - }); - } - - readTokenOptions(appId) { - return this.http.options(`${this.baseUrl}${appId}/tokens/`); - } -} - -export default Applications; diff --git a/awx/ui/src/api/models/Auth.js b/awx/ui/src/api/models/Auth.js deleted file mode 100644 index cae34f8fc322..000000000000 --- a/awx/ui/src/api/models/Auth.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class Auth extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/auth/'; - } -} - -export default Auth; diff --git a/awx/ui/src/api/models/Config.js b/awx/ui/src/api/models/Config.js deleted file mode 100644 index ae73f88d65f1..000000000000 --- a/awx/ui/src/api/models/Config.js +++ /dev/null @@ -1,22 +0,0 @@ -import Base from '../Base'; - -class Config extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/config/'; - this.read = this.read.bind(this); - } - - readSubscriptions(username, password) { - return this.http.post(`${this.baseUrl}subscriptions/`, { - subscriptions_username: username, - subscriptions_password: password, - }); - } - - attach(data) { - return this.http.post(`${this.baseUrl}attach/`, data); - } -} - -export default Config; diff --git a/awx/ui/src/api/models/CredentialInputSources.js b/awx/ui/src/api/models/CredentialInputSources.js deleted file mode 100644 index c82f27a22ec7..000000000000 --- a/awx/ui/src/api/models/CredentialInputSources.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class CredentialInputSources extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/credential_input_sources/'; - } -} - -export default CredentialInputSources; diff --git a/awx/ui/src/api/models/CredentialTypes.js b/awx/ui/src/api/models/CredentialTypes.js deleted file mode 100644 index 72f641c117bf..000000000000 --- a/awx/ui/src/api/models/CredentialTypes.js +++ /dev/null @@ -1,44 +0,0 @@ -import Base from '../Base'; - -class CredentialTypes extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/credential_types/'; - } - - async loadAllTypes( - acceptableKinds = [ - 'machine', - 'cloud', - 'net', - 'ssh', - 'vault', - 'kubernetes', - 'cryptography', - ] - ) { - const pageSize = 200; - // The number of credential types a user can have is unlimited. In practice, it is unlikely for - // users to have more than a page at the maximum request size. - const { - data: { next, results }, - } = await this.read({ page_size: pageSize }); - let nextResults = []; - if (next) { - const { data } = await this.read({ - page_size: pageSize, - page: 2, - }); - nextResults = data.results; - } - return results - .concat(nextResults) - .filter((type) => acceptableKinds.includes(type.kind)); - } - - test(id, data) { - return this.http.post(`${this.baseUrl}${id}/test/`, data); - } -} - -export default CredentialTypes; diff --git a/awx/ui/src/api/models/CredentialTypes.test.js b/awx/ui/src/api/models/CredentialTypes.test.js deleted file mode 100644 index 885c68a47a9f..000000000000 --- a/awx/ui/src/api/models/CredentialTypes.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import CredentialTypes from './CredentialTypes'; - -const typesData = [ - { id: 1, kind: 'machine' }, - { id: 2, kind: 'cloud' }, -]; - -describe('CredentialTypesAPI', () => { - test('should load all types', async () => { - const getPromise = () => - Promise.resolve({ - data: { - results: typesData, - }, - }); - const mockHttp = { get: jest.fn(getPromise) }; - const CredentialTypesAPI = new CredentialTypes(mockHttp); - - const types = await CredentialTypesAPI.loadAllTypes(); - - expect(mockHttp.get).toHaveBeenCalledTimes(1); - expect(mockHttp.get.mock.calls[0]).toEqual([ - `api/v2/credential_types/`, - { params: { page_size: 200 } }, - ]); - expect(types).toEqual(typesData); - }); - - test('should load all types (2 pages)', async () => { - const getPromise = () => - Promise.resolve({ - data: { - results: typesData, - next: 2, - }, - }); - const mockHttp = { get: jest.fn(getPromise) }; - const CredentialTypesAPI = new CredentialTypes(mockHttp); - - const types = await CredentialTypesAPI.loadAllTypes(); - - expect(mockHttp.get).toHaveBeenCalledTimes(2); - expect(mockHttp.get.mock.calls[0]).toEqual([ - `api/v2/credential_types/`, - { params: { page_size: 200 } }, - ]); - expect(mockHttp.get.mock.calls[1]).toEqual([ - `api/v2/credential_types/`, - { params: { page_size: 200, page: 2 } }, - ]); - expect(types).toHaveLength(4); - }); - - test('should filter by acceptable kinds', async () => { - const getPromise = () => - Promise.resolve({ - data: { - results: typesData, - }, - }); - const mockHttp = { get: jest.fn(getPromise) }; - const CredentialTypesAPI = new CredentialTypes(mockHttp); - - const types = await CredentialTypesAPI.loadAllTypes(['machine']); - - expect(types).toEqual([typesData[0]]); - }); -}); diff --git a/awx/ui/src/api/models/Credentials.js b/awx/ui/src/api/models/Credentials.js deleted file mode 100644 index 44bfbb9d0a4b..000000000000 --- a/awx/ui/src/api/models/Credentials.js +++ /dev/null @@ -1,62 +0,0 @@ -import Base from '../Base'; - -class Credentials extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/credentials/'; - - this.readAccessList = this.readAccessList.bind(this); - this.readAccessOptions = this.readAccessOptions.bind(this); - this.readInputSources = this.readInputSources.bind(this); - } - - readAccessList(id, params) { - return this.http.get(`${this.baseUrl}${id}/access_list/`, { - params, - }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - readInputSources(id) { - const maxRequests = 5; - let requestCounter = 0; - const fetchInputSources = async (pageNo = 1, inputSources = []) => { - try { - requestCounter++; - const { data } = await this.http.get( - `${this.baseUrl}${id}/input_sources/`, - { - params: { - page: pageNo, - page_size: 200, - }, - } - ); - if (data?.next && requestCounter <= maxRequests) { - return fetchInputSources( - pageNo + 1, - inputSources.concat(data.results) - ); - } - return Promise.resolve({ - data: { - results: inputSources.concat(data.results), - }, - }); - } catch (error) { - return Promise.reject(error); - } - }; - - return fetchInputSources(); - } - - test(id, data) { - return this.http.post(`${this.baseUrl}${id}/test/`, data); - } -} - -export default Credentials; diff --git a/awx/ui/src/api/models/Dashboard.js b/awx/ui/src/api/models/Dashboard.js deleted file mode 100644 index 06c00f191c32..000000000000 --- a/awx/ui/src/api/models/Dashboard.js +++ /dev/null @@ -1,16 +0,0 @@ -import Base from '../Base'; - -class Dashboard extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/dashboard/'; - } - - readJobGraph(params) { - return this.http.get(`${this.baseUrl}graphs/jobs/`, { - params, - }); - } -} - -export default Dashboard; diff --git a/awx/ui/src/api/models/ExecutionEnvironments.js b/awx/ui/src/api/models/ExecutionEnvironments.js deleted file mode 100644 index 8c2fff9a0c35..000000000000 --- a/awx/ui/src/api/models/ExecutionEnvironments.js +++ /dev/null @@ -1,20 +0,0 @@ -import Base from '../Base'; - -class ExecutionEnvironments extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/execution_environments/'; - } - - readUnifiedJobTemplates(id, params) { - return this.http.get(`${this.baseUrl}${id}/unified_job_templates/`, { - params, - }); - } - - readUnifiedJobTemplateOptions(id) { - return this.http.options(`${this.baseUrl}${id}/unified_job_templates/`); - } -} - -export default ExecutionEnvironments; diff --git a/awx/ui/src/api/models/Groups.js b/awx/ui/src/api/models/Groups.js deleted file mode 100644 index 6677a9e27317..000000000000 --- a/awx/ui/src/api/models/Groups.js +++ /dev/null @@ -1,59 +0,0 @@ -import Base from '../Base'; - -class Groups extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/groups/'; - - this.associateHost = this.associateHost.bind(this); - this.createHost = this.createHost.bind(this); - this.readAllHosts = this.readAllHosts.bind(this); - this.disassociateHost = this.disassociateHost.bind(this); - } - - associateHost(id, hostId) { - return this.http.post(`${this.baseUrl}${id}/hosts/`, { - id: hostId, - }); - } - - createHost(id, data) { - return this.http.post(`${this.baseUrl}${id}/hosts/`, data); - } - - readAllHosts(id, params) { - return this.http.get(`${this.baseUrl}${id}/all_hosts/`, { - params, - }); - } - - disassociateHost(id, host) { - return this.http.post(`${this.baseUrl}${id}/hosts/`, { - id: host.id, - disassociate: true, - }); - } - - readChildren(id, params) { - return this.http.get(`${this.baseUrl}${id}/children/`, { params }); - } - - associateChildGroup(id, childId) { - return this.http.post(`${this.baseUrl}${id}/children/`, { id: childId }); - } - - disassociateChildGroup(id, childId) { - return this.http.post(`${this.baseUrl}${id}/children/`, { - disassociate: id, - id: childId, - }); - } - - readPotentialGroups(id, params) { - return this.http.get(`${this.baseUrl}${id}/potential_children/`, { - params, - }); - } -} - -export default Groups; diff --git a/awx/ui/src/api/models/Hosts.js b/awx/ui/src/api/models/Hosts.js deleted file mode 100644 index 0422407da9e1..000000000000 --- a/awx/ui/src/api/models/Hosts.js +++ /dev/null @@ -1,43 +0,0 @@ -import Base from '../Base'; - -class Hosts extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/hosts/'; - - this.readFacts = this.readFacts.bind(this); - this.readAllGroups = this.readAllGroups.bind(this); - this.readGroupsOptions = this.readGroupsOptions.bind(this); - this.associateGroup = this.associateGroup.bind(this); - this.disassociateGroup = this.disassociateGroup.bind(this); - } - - readFacts(id) { - return this.http.get(`${this.baseUrl}${id}/ansible_facts/`); - } - - readAllGroups(id, params) { - return this.http.get(`${this.baseUrl}${id}/all_groups/`, { params }); - } - - readGroups(id, params) { - return this.http.get(`${this.baseUrl}${id}/groups/`, { params }); - } - - readGroupsOptions(id) { - return this.http.options(`${this.baseUrl}${id}/groups/`); - } - - associateGroup(id, groupId) { - return this.http.post(`${this.baseUrl}${id}/groups/`, { id: groupId }); - } - - disassociateGroup(id, group) { - return this.http.post(`${this.baseUrl}${id}/groups/`, { - id: group.id, - disassociate: true, - }); - } -} - -export default Hosts; diff --git a/awx/ui/src/api/models/InstanceGroups.js b/awx/ui/src/api/models/InstanceGroups.js deleted file mode 100644 index e28a1694e41a..000000000000 --- a/awx/ui/src/api/models/InstanceGroups.js +++ /dev/null @@ -1,41 +0,0 @@ -import Base from '../Base'; - -class InstanceGroups extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/instance_groups/'; - - this.associateInstance = this.associateInstance.bind(this); - this.disassociateInstance = this.disassociateInstance.bind(this); - this.readInstanceOptions = this.readInstanceOptions.bind(this); - this.readInstances = this.readInstances.bind(this); - this.readJobs = this.readJobs.bind(this); - } - - associateInstance(instanceGroupId, instanceId) { - return this.http.post(`${this.baseUrl}${instanceGroupId}/instances/`, { - id: instanceId, - }); - } - - disassociateInstance(instanceGroupId, instanceId) { - return this.http.post(`${this.baseUrl}${instanceGroupId}/instances/`, { - id: instanceId, - disassociate: true, - }); - } - - readInstances(id, params) { - return this.http.get(`${this.baseUrl}${id}/instances/`, { params }); - } - - readInstanceOptions(id) { - return this.http.options(`${this.baseUrl}${id}/instances/`); - } - - readJobs(id) { - return this.http.get(`${this.baseUrl}${id}/jobs/`); - } -} - -export default InstanceGroups; diff --git a/awx/ui/src/api/models/Instances.js b/awx/ui/src/api/models/Instances.js deleted file mode 100644 index 388bb2eb4e17..000000000000 --- a/awx/ui/src/api/models/Instances.js +++ /dev/null @@ -1,37 +0,0 @@ -import Base from '../Base'; - -class Instances extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/instances/'; - - this.readHealthCheckDetail = this.readHealthCheckDetail.bind(this); - this.healthCheck = this.healthCheck.bind(this); - this.readInstanceGroup = this.readInstanceGroup.bind(this); - this.deprovisionInstance = this.deprovisionInstance.bind(this); - } - - healthCheck(instanceId) { - return this.http.post(`${this.baseUrl}${instanceId}/health_check/`); - } - - readHealthCheckDetail(instanceId) { - return this.http.get(`${this.baseUrl}${instanceId}/health_check/`); - } - - readPeers(instanceId, params) { - return this.http.get(`${this.baseUrl}${instanceId}/peers/`, { params }); - } - - readInstanceGroup(instanceId) { - return this.http.get(`${this.baseUrl}${instanceId}/instance_groups/`); - } - - deprovisionInstance(instanceId) { - return this.http.patch(`${this.baseUrl}${instanceId}/`, { - node_state: 'deprovisioning', - }); - } -} - -export default Instances; diff --git a/awx/ui/src/api/models/Inventories.js b/awx/ui/src/api/models/Inventories.js deleted file mode 100644 index fd1653045f1b..000000000000 --- a/awx/ui/src/api/models/Inventories.js +++ /dev/null @@ -1,135 +0,0 @@ -import Base from '../Base'; -import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; - -class Inventories extends InstanceGroupsMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/inventories/'; - - this.readAccessList = this.readAccessList.bind(this); - this.readAccessOptions = this.readAccessOptions.bind(this); - this.readHosts = this.readHosts.bind(this); - this.readHostDetail = this.readHostDetail.bind(this); - this.readGroups = this.readGroups.bind(this); - this.readGroupsOptions = this.readGroupsOptions.bind(this); - this.promoteGroup = this.promoteGroup.bind(this); - } - - readAccessList(id, params) { - return this.http.get(`${this.baseUrl}${id}/access_list/`, { - params, - }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - createHost(id, data) { - return this.http.post(`${this.baseUrl}${id}/hosts/`, data); - } - - readHosts(id, params) { - return this.http.get(`${this.baseUrl}${id}/hosts/`, { - params, - }); - } - - async readHostDetail(inventoryId, hostId) { - const { - data: { results }, - } = await this.http.get( - `${this.baseUrl}${inventoryId}/hosts/?id=${hostId}` - ); - - if (Array.isArray(results) && results.length) { - return results[0]; - } - - throw new Error( - `How did you get here? Host not found for Inventory ID: ${inventoryId}` - ); - } - - readGroups(id, params) { - return this.http.get(`${this.baseUrl}${id}/groups/`, { - params, - }); - } - - readGroupsOptions(id) { - return this.http.options(`${this.baseUrl}${id}/groups/`); - } - - readHostsOptions(id) { - return this.http.options(`${this.baseUrl}${id}/hosts/`); - } - - promoteGroup(inventoryId, groupId) { - return this.http.post(`${this.baseUrl}${inventoryId}/groups/`, { - id: groupId, - disassociate: true, - }); - } - - readSources(inventoryId, params) { - return this.http.get(`${this.baseUrl}${inventoryId}/inventory_sources/`, { - params, - }); - } - - updateSources(inventoryId) { - return this.http.get( - `${this.baseUrl}${inventoryId}/update_inventory_sources/` - ); - } - - async readSourceDetail(inventoryId, sourceId) { - const { - data: { results }, - } = await this.http.get( - `${this.baseUrl}${inventoryId}/inventory_sources/?id=${sourceId}` - ); - - if (Array.isArray(results) && results.length) { - return results[0]; - } - - throw new Error( - `How did you get here? Source not found for Inventory ID: ${inventoryId}` - ); - } - - syncAllSources(inventoryId) { - return this.http.post( - `${this.baseUrl}${inventoryId}/update_inventory_sources/` - ); - } - - readAdHocOptions(inventoryId) { - return this.http.options(`${this.baseUrl}${inventoryId}/ad_hoc_commands/`); - } - - launchAdHocCommands(inventoryId, values) { - return this.http.post( - `${this.baseUrl}${inventoryId}/ad_hoc_commands/`, - values - ); - } - - associateLabel(id, label, orgId) { - return this.http.post(`${this.baseUrl}${id}/labels/`, { - name: label.name, - organization: orgId, - }); - } - - disassociateLabel(id, label) { - return this.http.post(`${this.baseUrl}${id}/labels/`, { - id: label.id, - disassociate: true, - }); - } -} - -export default Inventories; diff --git a/awx/ui/src/api/models/InventoryScripts.js b/awx/ui/src/api/models/InventoryScripts.js deleted file mode 100644 index 030238e67f9b..000000000000 --- a/awx/ui/src/api/models/InventoryScripts.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class InventoryScripts extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/inventory_scripts/'; - } -} - -export default InventoryScripts; diff --git a/awx/ui/src/api/models/InventorySources.js b/awx/ui/src/api/models/InventorySources.js deleted file mode 100644 index 66ad6dbc91cf..000000000000 --- a/awx/ui/src/api/models/InventorySources.js +++ /dev/null @@ -1,41 +0,0 @@ -import Base from '../Base'; -import NotificationsMixin from '../mixins/Notifications.mixin'; -import LaunchUpdateMixin from '../mixins/LaunchUpdate.mixin'; -import SchedulesMixin from '../mixins/Schedules.mixin'; - -class InventorySources extends LaunchUpdateMixin( - NotificationsMixin(SchedulesMixin(Base)) -) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/inventory_sources/'; - - this.createSchedule = this.createSchedule.bind(this); - this.createSyncStart = this.createSyncStart.bind(this); - this.destroyGroups = this.destroyGroups.bind(this); - this.destroyHosts = this.destroyHosts.bind(this); - } - - createSyncStart(sourceId, extraVars) { - return this.http.post(`${this.baseUrl}${sourceId}/update/`, { - extra_vars: extraVars, - }); - } - - readGroups(id) { - return this.http.get(`${this.baseUrl}${id}/groups/`); - } - - readHosts(id) { - return this.http.get(`${this.baseUrl}${id}/hosts/`); - } - - destroyGroups(id) { - return this.http.delete(`${this.baseUrl}${id}/groups/`); - } - - destroyHosts(id) { - return this.http.delete(`${this.baseUrl}${id}/hosts/`); - } -} -export default InventorySources; diff --git a/awx/ui/src/api/models/InventoryUpdates.js b/awx/ui/src/api/models/InventoryUpdates.js deleted file mode 100644 index 3d2e218720b6..000000000000 --- a/awx/ui/src/api/models/InventoryUpdates.js +++ /dev/null @@ -1,19 +0,0 @@ -import Base from '../Base'; -import RunnableMixin from '../mixins/Runnable.mixin'; - -class InventoryUpdates extends RunnableMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/inventory_updates/'; - this.createSyncCancel = this.createSyncCancel.bind(this); - } - - createSyncCancel(sourceId) { - return this.http.post(`${this.baseUrl}${sourceId}/cancel/`); - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } -} -export default InventoryUpdates; diff --git a/awx/ui/src/api/models/JobEvents.js b/awx/ui/src/api/models/JobEvents.js deleted file mode 100644 index dad879af8963..000000000000 --- a/awx/ui/src/api/models/JobEvents.js +++ /dev/null @@ -1,14 +0,0 @@ -import Base from '../Base'; - -class JobEvents extends Base { - constructor(http) { - super(http); - this.baseUrl = '/api/v2/job_events/'; - } - - readChildren(id, params) { - return this.http.get(`${this.baseUrl}${id}/children/`, { params }); - } -} - -export default JobEvents; diff --git a/awx/ui/src/api/models/JobTemplates.js b/awx/ui/src/api/models/JobTemplates.js deleted file mode 100644 index d2c1eb7a5d43..000000000000 --- a/awx/ui/src/api/models/JobTemplates.js +++ /dev/null @@ -1,93 +0,0 @@ -import Base from '../Base'; -import NotificationsMixin from '../mixins/Notifications.mixin'; -import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; -import LabelsMixin from '../mixins/Labels.mixin'; -import SchedulesMixin from '../mixins/Schedules.mixin'; - -class JobTemplates extends SchedulesMixin( - InstanceGroupsMixin(NotificationsMixin(LabelsMixin(Base))) -) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/job_templates/'; - - this.createSchedule = this.createSchedule.bind(this); - this.launch = this.launch.bind(this); - this.readLaunch = this.readLaunch.bind(this); - this.associateLabel = this.associateLabel.bind(this); - this.disassociateLabel = this.disassociateLabel.bind(this); - this.readCredentials = this.readCredentials.bind(this); - this.readAccessList = this.readAccessList.bind(this); - this.readAccessOptions = this.readAccessOptions.bind(this); - this.readWebhookKey = this.readWebhookKey.bind(this); - } - - launch(id, data) { - return this.http.post(`${this.baseUrl}${id}/launch/`, data); - } - - readTemplateOptions(id) { - return this.http.options(`${this.baseUrl}${id}/`); - } - - readLaunch(id) { - return this.http.get(`${this.baseUrl}${id}/launch/`); - } - - readCredentials(id, params) { - return this.http.get(`${this.baseUrl}${id}/credentials/`, { - params, - }); - } - - associateCredentials(id, credentialId) { - return this.http.post(`${this.baseUrl}${id}/credentials/`, { - id: credentialId, - }); - } - - disassociateCredentials(id, credentialId) { - return this.http.post(`${this.baseUrl}${id}/credentials/`, { - id: credentialId, - disassociate: true, - }); - } - - readAccessList(id, params) { - return this.http.get(`${this.baseUrl}${id}/access_list/`, { - params, - }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - readScheduleList(id, params) { - return this.http.get(`${this.baseUrl}${id}/schedules/`, { - params, - }); - } - - readSurvey(id) { - return this.http.get(`${this.baseUrl}${id}/survey_spec/`); - } - - updateSurvey(id, survey) { - return this.http.post(`${this.baseUrl}${id}/survey_spec/`, survey); - } - - destroySurvey(id) { - return this.http.delete(`${this.baseUrl}${id}/survey_spec/`); - } - - readWebhookKey(id) { - return this.http.get(`${this.baseUrl}${id}/webhook_key/`); - } - - updateWebhookKey(id) { - return this.http.post(`${this.baseUrl}${id}/webhook_key/`); - } -} - -export default JobTemplates; diff --git a/awx/ui/src/api/models/Jobs.js b/awx/ui/src/api/models/Jobs.js deleted file mode 100644 index 2e12c3ceb30b..000000000000 --- a/awx/ui/src/api/models/Jobs.js +++ /dev/null @@ -1,28 +0,0 @@ -import Base from '../Base'; -import RunnableMixin from '../mixins/Runnable.mixin'; - -class Jobs extends RunnableMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/jobs/'; - this.jobEventSlug = '/job_events/'; - } - - cancel(id) { - return this.http.post(`${this.baseUrl}${id}/cancel/`); - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } - - readDetail(id) { - return this.http.get(`${this.baseUrl}${id}/`); - } - - readChildrenSummary(id) { - return this.http.get(`${this.baseUrl}${id}/job_events/children_summary/`); - } -} - -export default Jobs; diff --git a/awx/ui/src/api/models/Labels.js b/awx/ui/src/api/models/Labels.js deleted file mode 100644 index ecc9d078faf8..000000000000 --- a/awx/ui/src/api/models/Labels.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class Labels extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/labels/'; - } -} - -export default Labels; diff --git a/awx/ui/src/api/models/Me.js b/awx/ui/src/api/models/Me.js deleted file mode 100644 index b9f7c6c03bfc..000000000000 --- a/awx/ui/src/api/models/Me.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class Me extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/me/'; - } -} - -export default Me; diff --git a/awx/ui/src/api/models/Mesh.js b/awx/ui/src/api/models/Mesh.js deleted file mode 100644 index d7ad08067c68..000000000000 --- a/awx/ui/src/api/models/Mesh.js +++ /dev/null @@ -1,9 +0,0 @@ -import Base from '../Base'; - -class Mesh extends Base { - constructor(http) { - super(http); - this.baseUrl = '/api/v2/mesh_visualizer/'; - } -} -export default Mesh; diff --git a/awx/ui/src/api/models/Metrics.js b/awx/ui/src/api/models/Metrics.js deleted file mode 100644 index 8fd742664599..000000000000 --- a/awx/ui/src/api/models/Metrics.js +++ /dev/null @@ -1,9 +0,0 @@ -import Base from '../Base'; - -class Metrics extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/metrics/'; - } -} -export default Metrics; diff --git a/awx/ui/src/api/models/NotificationTemplates.js b/awx/ui/src/api/models/NotificationTemplates.js deleted file mode 100644 index 6b83a46ed10f..000000000000 --- a/awx/ui/src/api/models/NotificationTemplates.js +++ /dev/null @@ -1,14 +0,0 @@ -import Base from '../Base'; - -class NotificationTemplates extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/notification_templates/'; - } - - test(id) { - return this.http.post(`${this.baseUrl}${id}/test/`); - } -} - -export default NotificationTemplates; diff --git a/awx/ui/src/api/models/Notifications.js b/awx/ui/src/api/models/Notifications.js deleted file mode 100644 index 6aca7210852c..000000000000 --- a/awx/ui/src/api/models/Notifications.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class Notifications extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/notifications/'; - } -} - -export default Notifications; diff --git a/awx/ui/src/api/models/Organizations.js b/awx/ui/src/api/models/Organizations.js deleted file mode 100644 index c20f72a1812f..000000000000 --- a/awx/ui/src/api/models/Organizations.js +++ /dev/null @@ -1,86 +0,0 @@ -import Base from '../Base'; -import NotificationsMixin from '../mixins/Notifications.mixin'; -import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; - -class Organizations extends InstanceGroupsMixin(NotificationsMixin(Base)) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/organizations/'; - } - - readAccessList(id, params) { - return this.http.get(`${this.baseUrl}${id}/access_list/`, { params }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - readTeams(id, params) { - return this.http.get(`${this.baseUrl}${id}/teams/`, { params }); - } - - readTeamsOptions(id) { - return this.http.options(`${this.baseUrl}${id}/teams/`); - } - - readGalaxyCredentials(id, params) { - return this.http.get(`${this.baseUrl}${id}/galaxy_credentials/`, { - params, - }); - } - - readExecutionEnvironments(id, params) { - return this.http.get(`${this.baseUrl}${id}/execution_environments/`, { - params, - }); - } - - readExecutionEnvironmentsOptions(id) { - return this.http.options(`${this.baseUrl}${id}/execution_environments/`); - } - - createUser(id, data) { - return this.http.post(`${this.baseUrl}${id}/users/`, data); - } - - readNotificationTemplatesApprovals(id, params) { - return this.http.get( - `${this.baseUrl}${id}/notification_templates_approvals/`, - { params } - ); - } - - associateNotificationTemplatesApprovals(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_approvals/`, - { id: notificationId } - ); - } - - disassociateNotificationTemplatesApprovals(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_approvals/`, - { id: notificationId, disassociate: true } - ); - } - - associateGalaxyCredential(resourceId, credentialId) { - return this.http.post(`${this.baseUrl}${resourceId}/galaxy_credentials/`, { - id: credentialId, - }); - } - - disassociateGalaxyCredential(resourceId, credentialId) { - return this.http.post(`${this.baseUrl}${resourceId}/galaxy_credentials/`, { - id: credentialId, - disassociate: true, - }); - } - - readAdmins(id, params) { - return this.http.get(`${this.baseUrl}${id}/admins/`, { params }); - } -} - -export default Organizations; diff --git a/awx/ui/src/api/models/Organizations.test.js b/awx/ui/src/api/models/Organizations.test.js deleted file mode 100644 index 728aa6f36550..000000000000 --- a/awx/ui/src/api/models/Organizations.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import { describeNotificationMixin } from '../../../testUtils/apiReusable'; -import Organizations from './Organizations'; - -describe('OrganizationsAPI', () => { - const orgId = 1; - let mockHttp; - let OrganizationsAPI; - beforeEach(() => { - const createPromise = () => Promise.resolve(); - mockHttp = { get: jest.fn(createPromise) }; - - OrganizationsAPI = new Organizations(mockHttp); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - test('read access list calls get with expected params', async () => { - const testParams = { foo: 'bar' }; - const testParamsDuplicates = { foo: ['bar', 'baz'] }; - - const mockBaseURL = `api/v2/organizations/${orgId}/access_list/`; - - await OrganizationsAPI.readAccessList(orgId); - await OrganizationsAPI.readAccessList(orgId, testParams); - await OrganizationsAPI.readAccessList(orgId, testParamsDuplicates); - - expect(mockHttp.get).toHaveBeenCalledTimes(3); - expect(mockHttp.get.mock.calls[0][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[0][1]).toEqual({ params: undefined }); - expect(mockHttp.get.mock.calls[1][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[1][1]).toEqual({ params: { foo: 'bar' } }); - expect(mockHttp.get.mock.calls[2][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[2][1]).toEqual({ - params: { foo: ['bar', 'baz'] }, - }); - }); - - test('read teams calls get with expected params', async () => { - const testParams = { foo: 'bar' }; - const testParamsDuplicates = { foo: ['bar', 'baz'] }; - - const mockBaseURL = `api/v2/organizations/${orgId}/teams/`; - - await OrganizationsAPI.readTeams(orgId); - await OrganizationsAPI.readTeams(orgId, testParams); - await OrganizationsAPI.readTeams(orgId, testParamsDuplicates); - - expect(mockHttp.get).toHaveBeenCalledTimes(3); - expect(mockHttp.get.mock.calls[0][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[0][1]).toEqual({ params: undefined }); - expect(mockHttp.get.mock.calls[1][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[1][1]).toEqual({ params: { foo: 'bar' } }); - expect(mockHttp.get.mock.calls[2][0]).toEqual(`${mockBaseURL}`); - expect(mockHttp.get.mock.calls[2][1]).toEqual({ - params: { foo: ['bar', 'baz'] }, - }); - }); -}); - -describeNotificationMixin(Organizations, 'Organizations[NotificationsMixin]'); diff --git a/awx/ui/src/api/models/ProjectUpdates.js b/awx/ui/src/api/models/ProjectUpdates.js deleted file mode 100644 index aead3b5d0a86..000000000000 --- a/awx/ui/src/api/models/ProjectUpdates.js +++ /dev/null @@ -1,15 +0,0 @@ -import Base from '../Base'; -import RunnableMixin from '../mixins/Runnable.mixin'; - -class ProjectUpdates extends RunnableMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/project_updates/'; - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } -} - -export default ProjectUpdates; diff --git a/awx/ui/src/api/models/Projects.js b/awx/ui/src/api/models/Projects.js deleted file mode 100644 index 437da8caca1c..000000000000 --- a/awx/ui/src/api/models/Projects.js +++ /dev/null @@ -1,47 +0,0 @@ -import Base from '../Base'; -import NotificationsMixin from '../mixins/Notifications.mixin'; -import LaunchUpdateMixin from '../mixins/LaunchUpdate.mixin'; -import SchedulesMixin from '../mixins/Schedules.mixin'; - -class Projects extends SchedulesMixin( - LaunchUpdateMixin(NotificationsMixin(Base)) -) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/projects/'; - - this.readAccessList = this.readAccessList.bind(this); - this.readAccessOptions = this.readAccessOptions.bind(this); - this.readInventories = this.readInventories.bind(this); - this.readPlaybooks = this.readPlaybooks.bind(this); - this.readSync = this.readSync.bind(this); - this.sync = this.sync.bind(this); - this.createSchedule = this.createSchedule.bind(this); - } - - readAccessList(id, params) { - return this.http.get(`${this.baseUrl}${id}/access_list/`, { params }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - readInventories(id) { - return this.http.get(`${this.baseUrl}${id}/inventories/`); - } - - readPlaybooks(id) { - return this.http.get(`${this.baseUrl}${id}/playbooks/`); - } - - readSync(id) { - return this.http.get(`${this.baseUrl}${id}/update/`); - } - - sync(id) { - return this.http.post(`${this.baseUrl}${id}/update/`); - } -} - -export default Projects; diff --git a/awx/ui/src/api/models/Roles.js b/awx/ui/src/api/models/Roles.js deleted file mode 100644 index 70195891e43d..000000000000 --- a/awx/ui/src/api/models/Roles.js +++ /dev/null @@ -1,23 +0,0 @@ -import Base from '../Base'; - -class Roles extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/roles/'; - } - - disassociateUserRole(roleId, userId) { - return this.http.post(`${this.baseUrl}${roleId}/users/`, { - disassociate: true, - id: userId, - }); - } - - disassociateTeamRole(roleId, teamId) { - return this.http.post(`${this.baseUrl}${roleId}/teams/`, { - disassociate: true, - id: teamId, - }); - } -} -export default Roles; diff --git a/awx/ui/src/api/models/Root.js b/awx/ui/src/api/models/Root.js deleted file mode 100644 index 4fe6384d8339..000000000000 --- a/awx/ui/src/api/models/Root.js +++ /dev/null @@ -1,38 +0,0 @@ -import Base from '../Base'; - -class Root extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/'; - this.redirectURL = '/api/v2/config/'; - } - - async login(username, password, redirect = this.redirectURL) { - const loginUrl = `${this.baseUrl}login/`; - const un = encodeURIComponent(username); - const pw = encodeURIComponent(password); - const next = encodeURIComponent(redirect); - - const data = `username=${un}&password=${pw}&next=${next}`; - const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; - - await this.http.get(loginUrl, { headers }); - const response = await this.http.post(loginUrl, data, { headers }); - - return response; - } - - logout() { - return this.http.get(`${this.baseUrl}logout/`); - } - - readAssetVariables() { - // TODO: There's better ways of doing this. Build tools, scripts, - // automation etc. should relocate this variable file to an importable - // location in src prior to building. That said, a raw http call - // works for now. - return this.http.get('static/media/default.strings.json'); - } -} - -export default Root; diff --git a/awx/ui/src/api/models/Root.test.js b/awx/ui/src/api/models/Root.test.js deleted file mode 100644 index dd2d820c4f38..000000000000 --- a/awx/ui/src/api/models/Root.test.js +++ /dev/null @@ -1,50 +0,0 @@ -import Root from './Root'; - -describe('RootAPI', () => { - let mockHttp; - let RootAPI; - beforeEach(() => { - const createPromise = () => Promise.resolve(); - mockHttp = { - get: jest.fn(createPromise), - post: jest.fn(createPromise), - }; - - RootAPI = new Root(mockHttp); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('login calls get and post with expected content headers', async () => { - const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; - - await RootAPI.login('username', 'password'); - - expect(mockHttp.get).toHaveBeenCalledTimes(1); - expect(mockHttp.get.mock.calls[0]).toContainEqual({ headers }); - - expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual({ headers }); - }); - - test('login sends expected data', async () => { - await RootAPI.login('foo', 'bar'); - await RootAPI.login('foo', 'bar', 'baz'); - - expect(mockHttp.post).toHaveBeenCalledTimes(2); - expect(mockHttp.post.mock.calls[0]).toContainEqual( - 'username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F' - ); - expect(mockHttp.post.mock.calls[1]).toContainEqual( - 'username=foo&password=bar&next=baz' - ); - }); - - test('logout calls expected http method', async () => { - await RootAPI.logout(); - - expect(mockHttp.get).toHaveBeenCalledTimes(1); - }); -}); diff --git a/awx/ui/src/api/models/Schedules.js b/awx/ui/src/api/models/Schedules.js deleted file mode 100644 index a32c62053804..000000000000 --- a/awx/ui/src/api/models/Schedules.js +++ /dev/null @@ -1,37 +0,0 @@ -import Base from '../Base'; -import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; -import LabelsMixin from '../mixins/Labels.mixin'; - -class Schedules extends InstanceGroupsMixin(LabelsMixin(Base)) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/schedules/'; - } - - createPreview(data) { - return this.http.post(`${this.baseUrl}preview/`, data); - } - - readCredentials(resourceId, params) { - return this.http.get(`${this.baseUrl}${resourceId}/credentials/`, params); - } - - associateCredential(resourceId, credentialId) { - return this.http.post(`${this.baseUrl}${resourceId}/credentials/`, { - id: credentialId, - }); - } - - disassociateCredential(resourceId, credentialId) { - return this.http.post(`${this.baseUrl}${resourceId}/credentials/`, { - id: credentialId, - disassociate: true, - }); - } - - readZoneInfo() { - return this.http.get(`${this.baseUrl}zoneinfo/`); - } -} - -export default Schedules; diff --git a/awx/ui/src/api/models/Settings.js b/awx/ui/src/api/models/Settings.js deleted file mode 100644 index 89aad94c00f4..000000000000 --- a/awx/ui/src/api/models/Settings.js +++ /dev/null @@ -1,42 +0,0 @@ -import Base from '../Base'; - -class Settings extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/settings/'; - } - - readAllOptions() { - return this.http.options(`${this.baseUrl}all/`); - } - - updateAll(data) { - return this.http.patch(`${this.baseUrl}all/`, data); - } - - readAll() { - return this.http.get(`${this.baseUrl}all/`); - } - - updateCategory(category, data) { - return this.http.patch(`${this.baseUrl}${category}/`, data); - } - - readCategory(category) { - return this.http.get(`${this.baseUrl}${category}/`); - } - - readCategoryOptions(category) { - return this.http.options(`${this.baseUrl}${category}/`); - } - - createTest(category, data) { - return this.http.post(`${this.baseUrl}${category}/test/`, data); - } - - revertCategory(category) { - return this.http.delete(`${this.baseUrl}${category}/`); - } -} - -export default Settings; diff --git a/awx/ui/src/api/models/SystemJobTemplates.js b/awx/ui/src/api/models/SystemJobTemplates.js deleted file mode 100644 index f99fbb71d758..000000000000 --- a/awx/ui/src/api/models/SystemJobTemplates.js +++ /dev/null @@ -1,18 +0,0 @@ -import Base from '../Base'; -import NotificationsMixin from '../mixins/Notifications.mixin'; -import SchedulesMixin from '../mixins/Schedules.mixin'; - -const Mixins = SchedulesMixin(NotificationsMixin(Base)); - -class SystemJobTemplates extends Mixins { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/system_job_templates/'; - } - - launch(id, data) { - return this.http.post(`${this.baseUrl}${id}/launch/`, data); - } -} - -export default SystemJobTemplates; diff --git a/awx/ui/src/api/models/SystemJobs.js b/awx/ui/src/api/models/SystemJobs.js deleted file mode 100644 index aadbfabeb290..000000000000 --- a/awx/ui/src/api/models/SystemJobs.js +++ /dev/null @@ -1,16 +0,0 @@ -import Base from '../Base'; - -import RunnableMixin from '../mixins/Runnable.mixin'; - -class SystemJobs extends RunnableMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/system_jobs/'; - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } -} - -export default SystemJobs; diff --git a/awx/ui/src/api/models/Teams.js b/awx/ui/src/api/models/Teams.js deleted file mode 100644 index 031718a0788f..000000000000 --- a/awx/ui/src/api/models/Teams.js +++ /dev/null @@ -1,47 +0,0 @@ -import Base from '../Base'; - -class Teams extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/teams/'; - } - - associateRole(teamId, roleId) { - return this.http.post(`${this.baseUrl}${teamId}/roles/`, { - id: roleId, - }); - } - - disassociateRole(teamId, roleId) { - return this.http.post(`${this.baseUrl}${teamId}/roles/`, { - id: roleId, - disassociate: true, - }); - } - - readRoles(teamId, params) { - return this.http.get(`${this.baseUrl}${teamId}/roles/`, { - params, - }); - } - - readRoleOptions(teamId) { - return this.http.options(`${this.baseUrl}${teamId}/roles/`); - } - - readAccessList(teamId, params) { - return this.http.get(`${this.baseUrl}${teamId}/access_list/`, { - params, - }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - readUsersAccessOptions(teamId) { - return this.http.options(`${this.baseUrl}${teamId}/users/`); - } -} - -export default Teams; diff --git a/awx/ui/src/api/models/Teams.test.js b/awx/ui/src/api/models/Teams.test.js deleted file mode 100644 index 0cfbf652f2db..000000000000 --- a/awx/ui/src/api/models/Teams.test.js +++ /dev/null @@ -1,43 +0,0 @@ -import Teams from './Teams'; - -describe('TeamsAPI', () => { - const teamId = 1; - const roleId = 7; - - let TeamsAPI; - let mockHttp; - - beforeEach(() => { - const createPromise = () => Promise.resolve(); - mockHttp = { post: jest.fn(createPromise) }; - - TeamsAPI = new Teams(mockHttp); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - test('associate role calls post with expected params', async () => { - await TeamsAPI.associateRole(teamId, roleId); - - expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual( - `api/v2/teams/${teamId}/roles/`, - { id: roleId } - ); - }); - - test('read teams calls post with expected params', async () => { - await TeamsAPI.disassociateRole(teamId, roleId); - - expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual( - `api/v2/teams/${teamId}/roles/`, - { - id: roleId, - disassociate: true, - } - ); - }); -}); diff --git a/awx/ui/src/api/models/Tokens.js b/awx/ui/src/api/models/Tokens.js deleted file mode 100644 index 2b1027d2a2bf..000000000000 --- a/awx/ui/src/api/models/Tokens.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class Tokens extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/tokens/'; - } -} - -export default Tokens; diff --git a/awx/ui/src/api/models/UnifiedJobTemplates.js b/awx/ui/src/api/models/UnifiedJobTemplates.js deleted file mode 100644 index 24ea77a0e6d0..000000000000 --- a/awx/ui/src/api/models/UnifiedJobTemplates.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class UnifiedJobTemplates extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/unified_job_templates/'; - } -} - -export default UnifiedJobTemplates; diff --git a/awx/ui/src/api/models/UnifiedJobs.js b/awx/ui/src/api/models/UnifiedJobs.js deleted file mode 100644 index 23337f0baf97..000000000000 --- a/awx/ui/src/api/models/UnifiedJobs.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class UnifiedJobs extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/unified_jobs/'; - } -} - -export default UnifiedJobs; diff --git a/awx/ui/src/api/models/Users.js b/awx/ui/src/api/models/Users.js deleted file mode 100644 index 8fa84ca7a3b1..000000000000 --- a/awx/ui/src/api/models/Users.js +++ /dev/null @@ -1,75 +0,0 @@ -import Base from '../Base'; - -class Users extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/users/'; - } - - associateRole(userId, roleId) { - return this.http.post(`${this.baseUrl}${userId}/roles/`, { - id: roleId, - }); - } - - createToken(userId, data) { - return this.http.post(`${this.baseUrl}${userId}/authorized_tokens/`, data); - } - - disassociateRole(userId, roleId) { - return this.http.post(`${this.baseUrl}${userId}/roles/`, { - id: roleId, - disassociate: true, - }); - } - - readOrganizations(userId, params) { - return this.http.get(`${this.baseUrl}${userId}/organizations/`, { - params, - }); - } - - readOrganizationOptions(userId, params) { - return this.http.options(`${this.baseUrl}${userId}/organizations/`, { - params, - }); - } - - readRoles(userId, params) { - return this.http.get(`${this.baseUrl}${userId}/roles/`, { - params, - }); - } - - readRoleOptions(userId) { - return this.http.options(`${this.baseUrl}${userId}/roles/`); - } - - readTeams(userId, params) { - return this.http.get(`${this.baseUrl}${userId}/teams/`, { - params, - }); - } - - readTeamsOptions(userId) { - return this.http.options(`${this.baseUrl}${userId}/teams/`); - } - - readTokens(userId, params) { - return this.http.get(`${this.baseUrl}${userId}/tokens/`, { - params, - }); - } - - readAdminOfOrganizations(userId, params) { - return this.http.get(`${this.baseUrl}${userId}/admin_of_organizations/`, { - params, - }); - } - - readTokenOptions(userId) { - return this.http.options(`${this.baseUrl}${userId}/tokens/`); - } -} - -export default Users; diff --git a/awx/ui/src/api/models/Users.test.js b/awx/ui/src/api/models/Users.test.js deleted file mode 100644 index 42af9424230f..000000000000 --- a/awx/ui/src/api/models/Users.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import Users from './Users'; - -describe('UsersAPI', () => { - const userId = 1; - const roleId = 7; - let UsersAPI; - let mockHttp; - beforeEach(() => { - const createPromise = () => Promise.resolve(); - mockHttp = { post: jest.fn(createPromise) }; - UsersAPI = new Users(mockHttp); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - test('associate role calls post with expected params', async () => { - await UsersAPI.associateRole(userId, roleId); - - expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual( - `api/v2/users/${userId}/roles/`, - { id: roleId } - ); - }); - - test('read users calls post with expected params', async () => { - await UsersAPI.disassociateRole(userId, roleId); - - expect(mockHttp.post).toHaveBeenCalledTimes(1); - expect(mockHttp.post.mock.calls[0]).toContainEqual( - `api/v2/users/${userId}/roles/`, - { - id: roleId, - disassociate: true, - } - ); - }); -}); diff --git a/awx/ui/src/api/models/WorkflowApprovalTemplates.js b/awx/ui/src/api/models/WorkflowApprovalTemplates.js deleted file mode 100644 index 53ca7a74c250..000000000000 --- a/awx/ui/src/api/models/WorkflowApprovalTemplates.js +++ /dev/null @@ -1,10 +0,0 @@ -import Base from '../Base'; - -class WorkflowApprovalTemplates extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/workflow_approval_templates/'; - } -} - -export default WorkflowApprovalTemplates; diff --git a/awx/ui/src/api/models/WorkflowApprovals.js b/awx/ui/src/api/models/WorkflowApprovals.js deleted file mode 100644 index fc0581f8cece..000000000000 --- a/awx/ui/src/api/models/WorkflowApprovals.js +++ /dev/null @@ -1,18 +0,0 @@ -import Base from '../Base'; - -class WorkflowApprovals extends Base { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/workflow_approvals/'; - } - - approve(id) { - return this.http.post(`${this.baseUrl}${id}/approve/`); - } - - deny(id) { - return this.http.post(`${this.baseUrl}${id}/deny/`); - } -} - -export default WorkflowApprovals; diff --git a/awx/ui/src/api/models/WorkflowJobTemplateNodes.js b/awx/ui/src/api/models/WorkflowJobTemplateNodes.js deleted file mode 100644 index fce36ad51666..000000000000 --- a/awx/ui/src/api/models/WorkflowJobTemplateNodes.js +++ /dev/null @@ -1,75 +0,0 @@ -import Base from '../Base'; -import InstanceGroupsMixin from '../mixins/InstanceGroups.mixin'; -import LabelsMixin from '../mixins/Labels.mixin'; - -class WorkflowJobTemplateNodes extends LabelsMixin(InstanceGroupsMixin(Base)) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/workflow_job_template_nodes/'; - } - - createApprovalTemplate(id, data) { - return this.http.post( - `${this.baseUrl}${id}/create_approval_template/`, - data - ); - } - - associateSuccessNode(id, idToAssociate) { - return this.http.post(`${this.baseUrl}${id}/success_nodes/`, { - id: idToAssociate, - }); - } - - associateFailureNode(id, idToAssociate) { - return this.http.post(`${this.baseUrl}${id}/failure_nodes/`, { - id: idToAssociate, - }); - } - - associateAlwaysNode(id, idToAssociate) { - return this.http.post(`${this.baseUrl}${id}/always_nodes/`, { - id: idToAssociate, - }); - } - - disassociateSuccessNode(id, idToDissociate) { - return this.http.post(`${this.baseUrl}${id}/success_nodes/`, { - id: idToDissociate, - disassociate: true, - }); - } - - disassociateFailuresNode(id, idToDissociate) { - return this.http.post(`${this.baseUrl}${id}/failure_nodes/`, { - id: idToDissociate, - disassociate: true, - }); - } - - disassociateAlwaysNode(id, idToDissociate) { - return this.http.post(`${this.baseUrl}${id}/always_nodes/`, { - id: idToDissociate, - disassociate: true, - }); - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } - - associateCredentials(id, credentialId) { - return this.http.post(`${this.baseUrl}${id}/credentials/`, { - id: credentialId, - }); - } - - disassociateCredentials(id, credentialId) { - return this.http.post(`${this.baseUrl}${id}/credentials/`, { - id: credentialId, - disassociate: true, - }); - } -} - -export default WorkflowJobTemplateNodes; diff --git a/awx/ui/src/api/models/WorkflowJobTemplates.js b/awx/ui/src/api/models/WorkflowJobTemplates.js deleted file mode 100644 index 430b8caed2da..000000000000 --- a/awx/ui/src/api/models/WorkflowJobTemplates.js +++ /dev/null @@ -1,110 +0,0 @@ -import Base from '../Base'; -import SchedulesMixin from '../mixins/Schedules.mixin'; -import NotificationsMixin from '../mixins/Notifications.mixin'; -import LabelsMixin from '../mixins/Labels.mixin'; - -class WorkflowJobTemplates extends SchedulesMixin( - NotificationsMixin(LabelsMixin(Base)) -) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/workflow_job_templates/'; - this.createSchedule = this.createSchedule.bind(this); - } - - readWebhookKey(id) { - return this.http.get(`${this.baseUrl}${id}/webhook_key/`); - } - - readWorkflowJobTemplateOptions(id) { - return this.http.options(`${this.baseUrl}${id}/`); - } - - updateWebhookKey(id) { - return this.http.post(`${this.baseUrl}${id}/webhook_key/`); - } - - associateLabel(id, label, orgId) { - return this.http.post(`${this.baseUrl}${id}/labels/`, { - name: label.name, - organization: orgId, - }); - } - - createNode(id, data) { - return this.http.post(`${this.baseUrl}${id}/workflow_nodes/`, data); - } - - disassociateLabel(id, label) { - return this.http.post(`${this.baseUrl}${id}/labels/`, { - id: label.id, - disassociate: true, - }); - } - - launch(id, data) { - return this.http.post(`${this.baseUrl}${id}/launch/`, data); - } - - readLaunch(id) { - return this.http.get(`${this.baseUrl}${id}/launch/`); - } - - readNodes(id, params) { - return this.http.get(`${this.baseUrl}${id}/workflow_nodes/`, { - params, - }); - } - - readAccessList(id, params) { - return this.http.get(`${this.baseUrl}${id}/access_list/`, { - params, - }); - } - - readAccessOptions(id) { - return this.http.options(`${this.baseUrl}${id}/access_list/`); - } - - readSurvey(id) { - return this.http.get(`${this.baseUrl}${id}/survey_spec/`); - } - - updateSurvey(id, survey) { - return this.http.post(`${this.baseUrl}${id}/survey_spec/`, survey); - } - - destroySurvey(id) { - return this.http.delete(`${this.baseUrl}${id}/survey_spec/`); - } - - readNotificationTemplatesApprovals(id, params) { - return this.http.get( - `${this.baseUrl}${id}/notification_templates_approvals/`, - { - params, - } - ); - } - - associateNotificationTemplatesApprovals(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_approvals/`, - { - id: notificationId, - } - ); - } - - disassociateNotificationTemplatesApprovals(resourceId, notificationId) { - return this.http.post( - `${this.baseUrl}${resourceId}/notification_templates_approvals/`, - { - id: notificationId, - disassociate: true, - } - ); - } -} - -export default WorkflowJobTemplates; diff --git a/awx/ui/src/api/models/WorkflowJobs.js b/awx/ui/src/api/models/WorkflowJobs.js deleted file mode 100644 index 1214368401ec..000000000000 --- a/awx/ui/src/api/models/WorkflowJobs.js +++ /dev/null @@ -1,19 +0,0 @@ -import Base from '../Base'; -import RunnableMixin from '../mixins/Runnable.mixin'; - -class WorkflowJobs extends RunnableMixin(Base) { - constructor(http) { - super(http); - this.baseUrl = 'api/v2/workflow_jobs/'; - } - - readNodes(id, params) { - return this.http.get(`${this.baseUrl}${id}/workflow_nodes/`, { params }); - } - - readCredentials(id) { - return this.http.get(`${this.baseUrl}${id}/credentials/`); - } -} - -export default WorkflowJobs; diff --git a/awx/ui/src/border.css b/awx/ui/src/border.css deleted file mode 100644 index f69d3c79669b..000000000000 --- a/awx/ui/src/border.css +++ /dev/null @@ -1,11 +0,0 @@ -.pf-c-select .pf-c-select__toggle:before { - border-top: var(--pf-c-select__toggle--before--BorderTopWidth) solid - var(--pf-c-select__toggle--before--BorderTopColor); - border-right: var(--pf-c-select__toggle--before--BorderRightWidth) solid - var(--pf-c-select__toggle--before--BorderRightColor); - border-bottom: var(--pf-c-select__toggle--before--BorderBottomWidth) solid - var(--pf-c-select__toggle--before--BorderBottomColor); - border-left: var(--pf-c-select__toggle--before--BorderLeftWidth) solid - var(--pf-c-select__toggle--before--BorderLeftColor); -} -/* https://github.com/patternfly/patternfly-react/issues/5650 */ diff --git a/awx/ui/src/components/About/About.js b/awx/ui/src/components/About/About.js deleted file mode 100644 index b58c880b51da..000000000000 --- a/awx/ui/src/components/About/About.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { t } from '@lingui/macro'; -import { AboutModal } from '@patternfly/react-core'; -import useBrandName from 'hooks/useBrandName'; - -function About({ version, isOpen, onClose }) { - const brandName = useBrandName(); - - const createSpeechBubble = () => { - let text = ''; - if (typeof brandName === 'string' && brandName.length > 0) { - text = - brandName.indexOf('AWX') === -1 - ? `${brandName} Controller ${version}` - : `${brandName} ${version}`; - } - - let top = ''; - let bottom = ''; - - for (let i = 0; i < text.length; i++) { - top += '_'; - bottom += '-'; - } - - top = ` __${top}__ \n`; - text = `< ${text} >\n`; - bottom = ` --${bottom}-- `; - - return top + text + bottom; - }; - - const speechBubble = createSpeechBubble(); - const copyright = t`Copyright`; - const redHatInc = t`Red Hat, Inc.`; - - return ( - -
-        {speechBubble}
-        {`
-          \\
-          \\   ^__^
-              (oo)\\_______
-              (__)      A )\\
-                  ||----w |
-                  ||     ||
-                    `}
-      
-
- ); -} - -About.propTypes = { - isOpen: PropTypes.bool, - onClose: PropTypes.func.isRequired, - version: PropTypes.string, -}; - -About.defaultProps = { - isOpen: false, - version: null, -}; - -export default About; diff --git a/awx/ui/src/components/About/About.test.js b/awx/ui/src/components/About/About.test.js deleted file mode 100644 index 293e87678035..000000000000 --- a/awx/ui/src/components/About/About.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import About from './About'; - -jest.mock('../../hooks/useBrandName', () => ({ - __esModule: true, - default: () => ({ - current: 'AWX', - }), -})); - -describe('', () => { - test('should render AboutModal', () => { - const onClose = jest.fn(); - const wrapper = shallow(); - - const modal = wrapper.find('AboutModal'); - expect(modal).toHaveLength(1); - expect(modal.prop('onClose')).toEqual(onClose); - expect(modal.prop('productName')).toEqual({ current: 'AWX' }); - expect(modal.prop('isOpen')).toEqual(true); - }); -}); diff --git a/awx/ui/src/components/About/index.js b/awx/ui/src/components/About/index.js deleted file mode 100644 index 1ef71b80e713..000000000000 --- a/awx/ui/src/components/About/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './About'; diff --git a/awx/ui/src/components/AdHocCommands/AdHocCommands.js b/awx/ui/src/components/AdHocCommands/AdHocCommands.js deleted file mode 100644 index 6036053e079e..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocCommands.js +++ /dev/null @@ -1,172 +0,0 @@ -import React, { useCallback, useEffect, useState, useContext } from 'react'; -import { useHistory, useParams } from 'react-router-dom'; - -import { t } from '@lingui/macro'; -import PropTypes from 'prop-types'; -import { Button, DropdownItem, Tooltip } from '@patternfly/react-core'; - -import useRequest, { useDismissableError } from 'hooks/useRequest'; -import { InventoriesAPI, CredentialTypesAPI } from 'api'; - -import { KebabifiedContext } from 'contexts/Kebabified'; -import AlertModal from '../AlertModal'; -import ErrorDetail from '../ErrorDetail'; -import AdHocCommandsWizard from './AdHocCommandsWizard'; -import ContentError from '../ContentError'; - -function AdHocCommands({ - adHocItems, - hasListItems, - onLaunchLoading, - moduleOptions, -}) { - const history = useHistory(); - const { id } = useParams(); - - const [isWizardOpen, setIsWizardOpen] = useState(false); - const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext); - - useEffect(() => { - if (isKebabified) { - onKebabModalChange(isWizardOpen); - } - }, [isKebabified, isWizardOpen, onKebabModalChange]); - - const { - result: { credentialTypeId, organizationId }, - request: fetchData, - error: fetchError, - } = useRequest( - useCallback(async () => { - const [{ data }, cred] = await Promise.all([ - InventoriesAPI.readDetail(id), - CredentialTypesAPI.read({ namespace: 'ssh' }), - ]); - return { - credentialTypeId: cred.data.results[0].id, - organizationId: data.organization, - }; - }, [id]), - { organizationId: null } - ); - useEffect(() => { - fetchData(); - }, [fetchData]); - - const { - isLoading: isLaunchLoading, - error: launchError, - request: launchAdHocCommands, - } = useRequest( - useCallback( - async (values) => { - const { data } = await InventoriesAPI.launchAdHocCommands(id, values); - history.push(`/jobs/command/${data.id}/output`); - }, - - [id, history] - ) - ); - - const { error, dismissError } = useDismissableError( - launchError || fetchError - ); - - const handleSubmit = async (values) => { - const { - credentials, - credential_passwords: { become_password, ssh_password, ssh_key_unlock }, - execution_environment, - ...remainingValues - } = values; - const newCredential = credentials[0].id; - - const manipulatedValues = { - credential: newCredential, - become_password, - ssh_password, - ssh_key_unlock, - execution_environment: execution_environment[0]?.id, - ...remainingValues, - }; - await launchAdHocCommands(manipulatedValues); - }; - useEffect( - () => onLaunchLoading(isLaunchLoading), - [isLaunchLoading, onLaunchLoading] - ); - - if (error && isWizardOpen) { - return ( - { - dismissError(); - setIsWizardOpen(false); - }} - > - {launchError ? ( - <> - {t`Failed to launch job.`} - - - ) : ( - - )} - - ); - } - return ( - // render buttons for drop down and for toolbar - // if modal is open render the modal - <> - - {isKebabified ? ( - setIsWizardOpen(true)} - ouiaId="run-command-dropdown-item" - > - {t`Run Command`} - - ) : ( - - )} - - - {isWizardOpen && ( - setIsWizardOpen(false)} - onLaunch={handleSubmit} - onDismissError={() => dismissError()} - /> - )} - - ); -} - -AdHocCommands.propTypes = { - adHocItems: PropTypes.arrayOf(PropTypes.object).isRequired, - hasListItems: PropTypes.bool.isRequired, - onLaunchLoading: PropTypes.func.isRequired, - moduleOptions: PropTypes.arrayOf(PropTypes.array).isRequired, -}; - -export default AdHocCommands; diff --git a/awx/ui/src/components/AdHocCommands/AdHocCommands.test.js b/awx/ui/src/components/AdHocCommands/AdHocCommands.test.js deleted file mode 100644 index 6e51fb3522e7..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocCommands.test.js +++ /dev/null @@ -1,453 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { - CredentialTypesAPI, - InventoriesAPI, - CredentialsAPI, - ExecutionEnvironmentsAPI, - RootAPI, -} from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AdHocCommands from './AdHocCommands'; - -jest.mock('../../api/models/CredentialTypes'); -jest.mock('../../api/models/Inventories'); -jest.mock('../../api/models/Credentials'); -jest.mock('../../api/models/ExecutionEnvironments'); -jest.mock('../../api/models/Root'); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useParams: () => ({ - id: 1, - }), -})); -const credentials = [ - { id: 1, kind: 'cloud', name: 'Cred 1', url: 'www.google.com' }, - { id: 2, kind: 'ssh', name: 'Cred 2', url: 'www.google.com' }, - { id: 3, kind: 'Ansible', name: 'Cred 3', url: 'www.google.com' }, - { id: 4, kind: 'Machine', name: 'Cred 4', url: 'www.google.com' }, - { id: 5, kind: 'Machine', name: 'Cred 5', url: 'www.google.com' }, -]; - -const adHocItems = [ - { - name: 'Inventory 1 Org 0', - }, - { name: 'Inventory 2 Org 0' }, -]; - -describe('', () => { - beforeEach(() => { - RootAPI.readAssetVariables.mockResolvedValue({ - data: { - BRAND_NAME: 'AWX', - }, - }); - CredentialTypesAPI.read.mockResolvedValue({ - data: { count: 1, results: [{ id: 1, name: 'cred' }] }, - }); - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'EE1 1', url: 'wwww.google.com' }, - { id: 2, name: 'EE2', url: 'wwww.google.com' }, - ], - count: 2, - }, - }); - }); - let wrapper; - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('mounts successfully', async () => { - await act(async () => { - wrapper = mountWithContexts( - jest.fn()} - moduleOptions={[ - ['command', 'command'], - ['shell', 'shell'], - ]} - /> - ); - }); - expect(wrapper.find('AdHocCommands').length).toBe(1); - }); - - test('should open the wizard', async () => { - InventoriesAPI.readDetail.mockResolvedValue({ data: { organization: 1 } }); - CredentialTypesAPI.read.mockResolvedValue({ - data: { results: [{ id: 1 }] }, - }); - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'EE1 1', url: 'wwww.google.com' }, - { id: 2, name: 'EE2', url: 'wwww.google.com' }, - ], - count: 2, - }, - }); - await act(async () => { - wrapper = mountWithContexts( - jest.fn()} - /> - ); - }); - await waitForElement( - wrapper, - 'button[aria-label="Run Command"]', - (el) => el.length === 1 - ); - await act(async () => - wrapper.find('button[aria-label="Run Command"]').prop('onClick')() - ); - - wrapper.update(); - - expect(wrapper.find('AdHocCommandsWizard').length).toBe(1); - }); - - test('should submit properly', async () => { - InventoriesAPI.launchAdHocCommands.mockResolvedValue({ data: { id: 1 } }); - InventoriesAPI.readDetail.mockResolvedValue({ - data: { organization: 1 }, - }); - - CredentialsAPI.read.mockResolvedValue({ - data: { - results: credentials, - count: 5, - }, - }); - CredentialsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'EE1 1', url: 'wwww.google.com' }, - { id: 2, name: 'EE2', url: 'wwww.google.com' }, - ], - count: 2, - }, - }); - ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {}, POST: {} } }, - }); - await act(async () => { - wrapper = mountWithContexts( - jest.fn()} - /> - ); - }); - await waitForElement( - wrapper, - 'button[aria-label="Run Command"]', - (el) => el.length === 1 - ); - await act(async () => - wrapper.find('button[aria-label="Run Command"]').prop('onClick')() - ); - wrapper.update(); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { value: 'foo', name: 'module_args' }, - }); - wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1); - }); - - wrapper.update(); - - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0); - - // second step of wizard - - await act(async () => { - wrapper.find('td#check-action-item-2').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="EE2"]').prop('isSelected') - ).toBe(true); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - // third step of wizard - await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0); - - await act(async () => { - wrapper.find('td#check-action-item-4').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="Cred 4"]').prop('isSelected') - ).toBe(true); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // fourth step - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - expect(InventoriesAPI.launchAdHocCommands).toBeCalledWith(1, { - module_args: 'foo', - diff_mode: false, - credential: 4, - become_password: undefined, - job_type: 'run', - become_enabled: '', - extra_vars: '---', - forks: 0, - limit: 'Inventory 1 Org 0, Inventory 2 Org 0', - module_name: 'command', - ssh_key_unlock: undefined, - ssh_password: undefined, - verbosity: 1, - execution_environment: 2, - }); - }); - - test('should throw error on submission properly', async () => { - InventoriesAPI.launchAdHocCommands.mockRejectedValue( - new Error({ - response: { - config: { - method: 'post', - url: '/api/v2/inventories/1/ad_hoc_commands', - }, - data: 'An error occurred', - status: 403, - }, - }) - ); - InventoriesAPI.readDetail.mockResolvedValue({ - data: { organization: 1 }, - }); - CredentialTypesAPI.read.mockResolvedValue({ - data: { - results: [ - { - id: 1, - }, - ], - }, - }); - CredentialsAPI.read.mockResolvedValue({ - data: { - results: credentials, - count: 5, - }, - }); - CredentialsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { - id: 1, - name: 'EE1 1', - url: 'wwww.google.com', - }, - { - id: 2, - name: 'EE2', - url: 'wwww.google.com', - }, - ], - count: 2, - }, - }); - ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {}, POST: {} } }, - }); - await act(async () => { - wrapper = mountWithContexts( - jest.fn()} - /> - ); - }); - await waitForElement( - wrapper, - 'button[aria-label="Run Command"]', - (el) => el.length === 1 - ); - await act(async () => - wrapper.find('button[aria-label="Run Command"]').prop('onClick')() - ); - wrapper.update(); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { - value: 'foo', - name: 'module_args', - }, - }); - wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1); - }); - - wrapper.update(); - - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0); - - // second step of wizard - - await act(async () => { - wrapper.find('td#check-action-item-2').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="EE2"]').prop('isSelected') - ).toBe(true); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - // third step of wizard - await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 0); - - await act(async () => { - wrapper.find('td#check-action-item-4').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="Cred 4"]').prop('isSelected') - ).toBe(true); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // fourth step of wizard - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - await waitForElement(wrapper, 'ErrorDetail', (el) => el.length > 0); - }); - - test('should disable run command button due to lack of list items', async () => { - InventoriesAPI.readHosts.mockResolvedValue({ - data: { results: [], count: 0 }, - }); - - await act(async () => { - wrapper = mountWithContexts( - jest.fn()} - /> - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - const runCommandsButton = wrapper.find('button[aria-label="Run Command"]'); - expect(runCommandsButton.prop('disabled')).toBe(true); - }); - - test('should open alert modal when error on fetching data', async () => { - InventoriesAPI.readDetail.mockRejectedValue( - new Error({ - response: { - config: { - method: 'options', - url: '/api/v2/inventories/1/', - }, - data: 'An error occurred', - status: 403, - }, - }) - ); - await act(async () => { - wrapper = mountWithContexts( - jest.fn()} - /> - ); - }); - await act(async () => wrapper.find('button').prop('onClick')()); - wrapper.update(); - expect(wrapper.find('ErrorDetail').length).toBe(1); - }); -}); diff --git a/awx/ui/src/components/AdHocCommands/AdHocCommandsWizard.js b/awx/ui/src/components/AdHocCommands/AdHocCommandsWizard.js deleted file mode 100644 index 94e4ff6e799d..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocCommandsWizard.js +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { t } from '@lingui/macro'; -import { withFormik, useFormikContext } from 'formik'; -import PropTypes from 'prop-types'; -import Wizard from '../Wizard'; -import useAdHocLaunchSteps from './useAdHocLaunchSteps'; - -function AdHocCommandsWizard({ - onLaunch, - moduleOptions, - onCloseWizard, - credentialTypeId, - organizationId, -}) { - const { setFieldTouched, values } = useFormikContext(); - - const { steps, validateStep, visitStep, visitAllSteps } = useAdHocLaunchSteps( - moduleOptions, - organizationId, - credentialTypeId - ); - - return ( - { - if (nextStep.id === 'preview') { - visitAllSteps(setFieldTouched); - } else { - visitStep(prevStep.prevId, setFieldTouched); - validateStep(nextStep.id); - } - }} - onClose={() => onCloseWizard()} - onSave={() => { - onLaunch(values); - }} - onGoToStep={(nextStep, prevStep) => { - if (nextStep.id === 'preview') { - visitAllSteps(setFieldTouched); - } else { - visitStep(prevStep.prevId, setFieldTouched); - validateStep(nextStep.id); - } - }} - steps={steps} - title={t`Run command`} - backButtonText={t`Back`} - cancelButtonText={t`Cancel`} - nextButtonText={t`Next`} - /> - ); -} - -const FormikApp = withFormik({ - mapPropsToValues({ adHocItems }) { - const adHocItemStrings = adHocItems.map((item) => item.name).join(', '); - return { - limit: adHocItemStrings || 'all', - credentials: [], - module_args: '', - verbosity: 0, - forks: 0, - diff_mode: false, - become_enabled: '', - module_name: '', - extra_vars: '---', - job_type: 'run', - credential_passwords: {}, - execution_environment: '', - }; - }, -})(AdHocCommandsWizard); - -FormikApp.propTypes = { - onLaunch: PropTypes.func.isRequired, - moduleOptions: PropTypes.arrayOf(PropTypes.array).isRequired, - onCloseWizard: PropTypes.func.isRequired, - credentialTypeId: PropTypes.number.isRequired, -}; -export default FormikApp; diff --git a/awx/ui/src/components/AdHocCommands/AdHocCommandsWizard.test.js b/awx/ui/src/components/AdHocCommands/AdHocCommandsWizard.test.js deleted file mode 100644 index 83d7e38bfcbb..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocCommandsWizard.test.js +++ /dev/null @@ -1,403 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { CredentialsAPI, ExecutionEnvironmentsAPI, RootAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AdHocCommandsWizard from './AdHocCommandsWizard'; - -jest.mock('../../api/models/CredentialTypes'); -jest.mock('../../api/models/Inventories'); -jest.mock('../../api/models/Credentials'); -jest.mock('../../api/models/ExecutionEnvironments'); -jest.mock('../../api/models/Root'); - -const moduleOptions = [ - ['command', 'command'], - ['shell', 'shell'], -]; -const adHocItems = [ - { name: 'Inventory 1' }, - { name: 'Inventory 2' }, - { name: 'inventory 3' }, -]; -describe('', () => { - let wrapper; - const onLaunch = jest.fn(); - beforeEach(async () => { - RootAPI.readAssetVariables.mockResolvedValue({ - data: { - BRAND_NAME: 'AWX', - }, - }); - await act(async () => { - wrapper = mountWithContexts( - {}} - credentialTypeId={1} - organizationId={1} - /> - ); - }); - }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should mount properly', async () => { - expect(wrapper.find('AdHocCommandsWizard').length).toBe(1); - }); - - test('launch button should be disabled', async () => { - waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0); - - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - wrapper.update(); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - wrapper.update(); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - wrapper.update(); - - expect(wrapper.find('AdHocPreviewStep').prop('hasErrors')).toBe(true); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(true); - }); - - test('launch button should become active', async () => { - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'EE 1', url: '' }, - { id: 2, name: 'EE 2', url: '' }, - ], - count: 2, - }, - }); - ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - CredentialsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'Cred 1', url: '' }, - { id: 2, name: 'Cred2', url: '' }, - ], - count: 2, - }, - }); - CredentialsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { value: 'foo', name: 'module_args' }, - }); - wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1); - }); - wrapper.update(); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - wrapper.update(); - - // step 2 - - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - expect(wrapper.find('CheckboxListItem').length).toBe(2); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - await act(async () => { - wrapper.find('td#check-action-item-1').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="EE 1"]').prop('isSelected') - ).toBe(true); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - wrapper.update(); - // step 3 - - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - expect(wrapper.find('CheckboxListItem').length).toBe(2); - - await act(async () => { - wrapper.find('td#check-action-item-1').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="Cred 1"]').prop('isSelected') - ).toBe(true); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - expect(onLaunch).toHaveBeenCalledWith({ - become_enabled: '', - credentials: [{ id: 1, name: 'Cred 1', url: '' }], - credential_passwords: {}, - diff_mode: false, - execution_environment: [{ id: 1, name: 'EE 1', url: '' }], - extra_vars: '---', - forks: 0, - job_type: 'run', - limit: 'Inventory 1, Inventory 2, inventory 3', - module_args: 'foo', - module_name: 'command', - verbosity: 1, - }); - }); - - test('should render credential passwords step', async () => { - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'EE 1', url: '' }, - { id: 2, name: 'EE 2', url: '' }, - ], - count: 2, - }, - }); - ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - CredentialsAPI.read.mockResolvedValue({ - data: { - results: [ - { - id: 1, - name: 'Cred 1', - url: '', - inputs: { password: 'ASK' }, - }, - { id: 2, name: 'Cred2', url: '' }, - ], - count: 2, - }, - }); - CredentialsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { value: 'foo', name: 'module_args' }, - }); - wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1); - }); - wrapper.update(); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - wrapper.update(); - - // step 2 - - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - expect(wrapper.find('CheckboxListItem').length).toBe(2); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - await act(async () => { - wrapper.find('td#check-action-item-1').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="EE 1"]').prop('isSelected') - ).toBe(true); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - wrapper.update(); - // step 3 - - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - expect(wrapper.find('CheckboxListItem').length).toBe(2); - - await act(async () => { - wrapper.find('td#check-action-item-1').find('input').simulate('click'); - }); - - wrapper.update(); - - expect( - wrapper.find('CheckboxListItem[label="Cred 1"]').prop('isSelected') - ).toBe(true); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // step 4 - - expect(wrapper.find('PasswordInput')).toHaveLength(1); - await act(async () => - wrapper - .find('TextInputBase[name="credential_passwords.ssh_password"]') - .prop('onChange')('', { - target: { - value: 'password', - name: 'credential_passwords.ssh_password', - }, - }) - ); - wrapper.update(); - expect( - wrapper - .find('TextInput[name="credential_passwords.ssh_password"]') - .prop('value') - ).toBe('password'); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // step 5 - - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - expect(onLaunch).toHaveBeenCalledWith({ - become_enabled: '', - credentials: [ - { id: 1, name: 'Cred 1', url: '', inputs: { password: 'ASK' } }, - ], - credential_passwords: { ssh_password: 'password' }, - diff_mode: false, - execution_environment: [{ id: 1, name: 'EE 1', url: '' }], - extra_vars: '---', - forks: 0, - job_type: 'run', - limit: 'Inventory 1, Inventory 2, inventory 3', - module_args: 'foo', - module_name: 'command', - verbosity: 1, - }); - }); - - test('should show error in navigation bar', async () => { - await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { value: '', name: 'module_args' }, - }); - }); - waitForElement(wrapper, 'ExclamationCircleIcon', (el) => el.length > 0); - }); - - test('expect credential step to throw error', async () => { - CredentialsAPI.read.mockRejectedValue( - new Error({ - response: { - config: { - method: 'get', - url: '/api/v2/credentials', - }, - data: 'An error occurred', - status: 403, - }, - }) - ); - CredentialsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - await waitForElement(wrapper, 'WizardNavItem', (el) => el.length > 0); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { value: 'foo', name: 'module_args' }, - }); - wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1); - }); - wrapper.update(); - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe( - false - ); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - wrapper.update(); - - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - - wrapper.update(); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/components/AdHocCommands/AdHocCredentialStep.js b/awx/ui/src/components/AdHocCommands/AdHocCredentialStep.js deleted file mode 100644 index 8001315cbf7f..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocCredentialStep.js +++ /dev/null @@ -1,155 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { useHistory } from 'react-router-dom'; -import { t } from '@lingui/macro'; -import styled from 'styled-components'; -import PropTypes from 'prop-types'; -import { useField } from 'formik'; -import { Form, FormGroup, Alert } from '@patternfly/react-core'; -import { CredentialsAPI } from 'api'; -import { getQSConfig, parseQueryString, mergeParams } from 'util/qs'; -import useRequest from 'hooks/useRequest'; -import { required } from 'util/validators'; -import { getSearchableKeys } from 'components/PaginatedTable'; -import Popover from '../Popover'; - -import ContentError from '../ContentError'; -import ContentLoading from '../ContentLoading'; -import OptionsList from '../OptionsList'; - -const CredentialErrorAlert = styled(Alert)` - margin-bottom: 20px; -`; - -const QS_CONFIG = getQSConfig('credentials', { - page: 1, - page_size: 5, - order_by: 'name', -}); - -function AdHocCredentialStep({ credentialTypeId }) { - const history = useHistory(); - const { - error, - isLoading, - request: fetchCredentials, - result: { - credentials, - credentialCount, - relatedSearchableKeys, - searchableKeys, - }, - } = useRequest( - useCallback(async () => { - const params = parseQueryString(QS_CONFIG, history.location.search); - - const [ - { - data: { results, count }, - }, - actionsResponse, - ] = await Promise.all([ - CredentialsAPI.read( - mergeParams(params, { credential_type: credentialTypeId }) - ), - CredentialsAPI.readOptions(), - ]); - - return { - credentials: results, - credentialCount: count, - relatedSearchableKeys: ( - actionsResponse?.data?.related_search_fields || [] - ).map((val) => val.slice(0, -8)), - searchableKeys: getSearchableKeys(actionsResponse.data.actions?.GET), - }; - }, [credentialTypeId, history.location.search]), - { - credentials: [], - credentialCount: 0, - relatedSearchableKeys: [], - searchableKeys: [], - } - ); - - useEffect(() => { - fetchCredentials(); - }, [fetchCredentials]); - - const [field, meta, helpers] = useField({ - name: 'credentials', - validate: required(null), - }); - - if (error) { - return ; - } - if (isLoading) { - return ; - } - return ( - <> - {meta.touched && meta.error && ( - - )} - - - } - > - { - helpers.setValue([value]); - }} - deselectItem={() => { - helpers.setValue([]); - }} - searchableKeys={searchableKeys} - relatedSearchableKeys={relatedSearchableKeys} - /> - - - - ); -} - -AdHocCredentialStep.propTypes = { - credentialTypeId: PropTypes.number.isRequired, -}; -export default AdHocCredentialStep; diff --git a/awx/ui/src/components/AdHocCommands/AdHocCredentialStep.test.js b/awx/ui/src/components/AdHocCommands/AdHocCredentialStep.test.js deleted file mode 100644 index b90346f2b304..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocCredentialStep.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { Formik } from 'formik'; -import { CredentialsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AdHocCredentialStep from './AdHocCredentialStep'; - -jest.mock('../../api/models/Credentials'); - -describe('', () => { - const onEnableLaunch = jest.fn(); - let wrapper; - beforeEach(async () => { - CredentialsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'Cred 1', url: 'wwww.google.com' }, - { id: 2, name: 'Cred2', url: 'wwww.google.com' }, - ], - count: 2, - }, - }); - CredentialsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - }); - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should mount properly', async () => { - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - }); - - test('should call api', async () => { - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - expect(CredentialsAPI.read).toHaveBeenCalled(); - expect(wrapper.find('CheckboxListItem').length).toBe(2); - }); -}); diff --git a/awx/ui/src/components/AdHocCommands/AdHocDetailsStep.js b/awx/ui/src/components/AdHocCommands/AdHocDetailsStep.js deleted file mode 100644 index 15cf1f82e837..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocDetailsStep.js +++ /dev/null @@ -1,285 +0,0 @@ -/* eslint-disable react/no-unescaped-entities */ -import React from 'react'; -import { t } from '@lingui/macro'; -import PropTypes from 'prop-types'; -import { useField } from 'formik'; -import { Form, FormGroup, Switch, Checkbox } from '@patternfly/react-core'; -import styled from 'styled-components'; -import { required } from 'util/validators'; -import useBrandName from 'hooks/useBrandName'; -import { VerbositySelectField } from 'components/VerbositySelectField'; -import AnsibleSelect from '../AnsibleSelect'; -import FormField from '../FormField'; -import { VariablesField } from '../CodeEditor'; -import { - FormColumnLayout, - FormFullWidthLayout, - FormCheckboxLayout, -} from '../FormLayout'; -import Popover from '../Popover'; - -const TooltipWrapper = styled.div` - text-align: left; -`; - -function AdHocDetailsStep({ moduleOptions }) { - const brandName = useBrandName(); - const [moduleNameField, moduleNameMeta, moduleNameHelpers] = useField({ - name: 'module_name', - validate: required(null), - }); - - const [variablesField] = useField('extra_vars'); - const [diffModeField, , diffModeHelpers] = useField('diff_mode'); - const [becomeEnabledField, , becomeEnabledHelpers] = - useField('become_enabled'); - const [, verbosityMeta] = useField({ - name: 'verbosity', - validate: required(null), - }); - - const argumentsRequired = - moduleNameField.value === 'command' || moduleNameField.value === 'shell'; - const [argumentsField, argumentsMeta, argumentsHelpers] = useField({ - name: 'module_args', - validate: argumentsRequired && required(null), - }); - - const isValid = argumentsRequired - ? (!argumentsMeta.error || !argumentsMeta.touched) && argumentsField.value - : true; - - return ( -
- - - - } - > - ({ - value: value[0], - label: value[0], - key: value[0], - })), - ]} - onChange={(event, value) => { - if (value !== 'command' && value !== 'shell') { - argumentsHelpers.setTouched(false); - } - moduleNameHelpers.setValue(value); - }} - /> - - argumentsHelpers.setTouched(true)} - isRequired={argumentsRequired} - tooltip={ - moduleNameField.value ? ( - <> - {t`These arguments are used with the specified module. You can find information about ${moduleNameField.value} by clicking `} - - {' '} - {t`here.`} - - - ) : ( - t`These arguments are used with the specified module.` - ) - } - /> - - - - {t`The pattern used to target hosts in the inventory. Leaving the field blank, all, and * will all target all hosts in the inventory. You can find more information about Ansible's host patterns`}{' '} - - {t`here`} - - - } - /> - - {t`The number of parallel or simultaneous processes to use while executing the playbook. Inputting no value will use the default value from the ansible configuration file. You can find more information`}{' '} - - {t`here.`} - - - } - /> - - - } - > - { - diffModeHelpers.setValue(!diffModeField.value); - }} - ouiaId="diff-mode-switch" - aria-label={t`toggle changes`} - /> - - - - - {t`Enable privilege escalation`} -   - - {t`Enables creation of a provisioning - callback URL. Using the URL a host can contact ${brandName} - and request a configuration update using this job - template`} -   - --become - {t`option to the`}   - ansible - {t`command`} -

- } - /> - - } - id="become_enabled" - ouiaId="become_enabled" - isChecked={becomeEnabledField.value} - onChange={(checked) => { - becomeEnabledHelpers.setValue(checked); - }} - /> -
-
-
- - -

- {t`Pass extra command line changes. There are two ansible command line parameters: `} -
- -e, --extra-vars -
- {t`Provide key/value pairs using either - YAML or JSON.`} -

- JSON: -
- -
-                    {'{'}
-                    {'\n  '}"somevar": "somevalue",
-                    {'\n  '}"password": "magic"
-                    {'\n'}
-                    {'}'}
-                  
-
- YAML: -
- -
-                    ---
-                    {'\n'}somevar: somevalue
-                    {'\n'}password: magic
-                  
-
- - } - label={t`Extra variables`} - aria-label={t`Extra variables`} - /> -
-
-
- ); -} - -AdHocDetailsStep.propTypes = { - moduleOptions: PropTypes.arrayOf(PropTypes.array).isRequired, -}; - -export default AdHocDetailsStep; diff --git a/awx/ui/src/components/AdHocCommands/AdHocDetailsStep.test.js b/awx/ui/src/components/AdHocCommands/AdHocDetailsStep.test.js deleted file mode 100644 index 2a78ffe0d0d1..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocDetailsStep.test.js +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { Formik } from 'formik'; -import { RootAPI } from 'api'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import DetailsStep from './AdHocDetailsStep'; - -jest.mock('../../api/models/Credentials'); -jest.mock('../../api/models/Root'); - -const verbosityOptions = [ - { key: -1, value: '', label: '', isDisabled: false }, - { key: 0, value: 0, label: '0', isDisabled: false }, - { key: 1, value: 1, label: '1', isDisabled: false }, -]; -const moduleOptions = [ - ['command', 'command'], - ['shell', 'shell'], -]; -const onLimitChange = jest.fn(); -const initialValues = { - limit: ['Inventory 1', 'inventory 2'], - credential: [], - module_args: '', - arguments: '', - verbosity: '', - forks: 0, - changes: false, - escalation: false, - extra_vars: '---', - module_name: 'shell', -}; - -describe('', () => { - let wrapper; - - beforeEach(() => { - RootAPI.readAssetVariables.mockResolvedValue({ - data: { - BRAND_NAME: 'AWX', - }, - }); - }); - - test('should mount properly', async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - }); - - test('should show all the fields', async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - expect(wrapper.find('FormGroup[label="Module"]').length).toBe(1); - expect(wrapper.find('FormField[label="Arguments"]').length).toBe(1); - expect(wrapper.find('FormGroup[label="Verbosity"]').length).toBe(1); - expect(wrapper.find('FormField[label="Limit"]').length).toBe(1); - expect(wrapper.find('FormField[name="forks"]').length).toBe(1); - expect(wrapper.find('FormGroup[label="Show changes"]').length).toBe(1); - expect(wrapper.find('FormGroup[name="become_enabled"]').length).toBe(1); - expect(wrapper.find('VariablesField').length).toBe(1); - }); - - test('shold update form values', async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - - await act(async () => { - wrapper.find('AnsibleSelect[name="module_name"]').prop('onChange')( - {}, - 'command' - ); - wrapper.find('input#module_args').simulate('change', { - target: { value: 'foo', name: 'module_args' }, - }); - wrapper.find('input#limit').simulate('change', { - target: { - value: 'Inventory 1, inventory 2, new inventory', - name: 'limit', - }, - }); - wrapper.find('AnsibleSelect[name="verbosity"]').prop('onChange')({}, 1); - - wrapper.find('TextInputBase[name="forks"]').simulate('change', { - target: { value: 10, name: 'forks' }, - }); - wrapper.find('Switch').invoke('onChange')(); - wrapper - .find('Checkbox[aria-label="Enable privilege escalation"]') - .invoke('onChange')(true, { - currentTarget: { value: true, type: 'change', checked: true }, - }); - }); - wrapper.update(); - expect( - wrapper.find('AnsibleSelect[name="module_name"]').prop('value') - ).toBe('command'); - expect(wrapper.find('input#module_args').prop('value')).toBe('foo'); - expect(wrapper.find('AnsibleSelect[name="verbosity"]').prop('value')).toBe( - 1 - ); - expect(wrapper.find('TextInputBase[name="forks"]').prop('value')).toBe(10); - expect(wrapper.find('TextInputBase[name="limit"]').prop('value')).toBe( - 'Inventory 1, inventory 2, new inventory' - ); - expect(wrapper.find('Switch').prop('isChecked')).toBe(true); - expect( - wrapper - .find('Checkbox[aria-label="Enable privilege escalation"]') - .prop('isChecked') - ).toBe(true); - }); -}); diff --git a/awx/ui/src/components/AdHocCommands/AdHocExecutionEnironmentStep.test.js b/awx/ui/src/components/AdHocCommands/AdHocExecutionEnironmentStep.test.js deleted file mode 100644 index a270226cdd38..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocExecutionEnironmentStep.test.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { Formik } from 'formik'; -import { ExecutionEnvironmentsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AdHocExecutionEnvironmentStep from './AdHocExecutionEnvironmentStep'; - -jest.mock('../../api/models/ExecutionEnvironments'); - -describe('', () => { - let wrapper; - beforeEach(async () => { - ExecutionEnvironmentsAPI.read.mockResolvedValue({ - data: { - results: [ - { id: 1, name: 'EE1 1', url: 'wwww.google.com' }, - { id: 2, name: 'EE2', url: 'wwww.google.com' }, - ], - count: 2, - }, - }); - ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({ - data: { actions: { GET: {} } }, - }); - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should mount properly', async () => { - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - }); - - test('should call api', async () => { - await waitForElement(wrapper, 'OptionsList', (el) => el.length > 0); - expect(ExecutionEnvironmentsAPI.read).toHaveBeenCalled(); - expect(wrapper.find('CheckboxListItem').length).toBe(2); - }); -}); diff --git a/awx/ui/src/components/AdHocCommands/AdHocExecutionEnvironmentStep.js b/awx/ui/src/components/AdHocCommands/AdHocExecutionEnvironmentStep.js deleted file mode 100644 index 3ced0adbe890..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocExecutionEnvironmentStep.js +++ /dev/null @@ -1,140 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { useHistory } from 'react-router-dom'; -import { t } from '@lingui/macro'; -import { useField } from 'formik'; -import { Form, FormGroup } from '@patternfly/react-core'; -import { ExecutionEnvironmentsAPI } from 'api'; - -import { parseQueryString, getQSConfig, mergeParams } from 'util/qs'; -import { getSearchableKeys } from 'components/PaginatedTable'; -import useRequest from 'hooks/useRequest'; -import Popover from '../Popover'; -import ContentError from '../ContentError'; -import ContentLoading from '../ContentLoading'; -import OptionsList from '../OptionsList'; - -const QS_CONFIG = getQSConfig('execution_environments', { - page: 1, - page_size: 5, - order_by: 'name', -}); -function AdHocExecutionEnvironmentStep({ organizationId }) { - const history = useHistory(); - const [executionEnvironmentField, , executionEnvironmentHelpers] = useField( - 'execution_environment' - ); - const { - error, - isLoading, - request: fetchExecutionEnvironments, - result: { - executionEnvironments, - executionEnvironmentsCount, - relatedSearchableKeys, - searchableKeys, - }, - } = useRequest( - useCallback(async () => { - const params = parseQueryString(QS_CONFIG, history.location.search); - const globallyAvailableParams = { or__organization__isnull: 'True' }; - const organizationIdParams = organizationId - ? { or__organization__id: organizationId } - : {}; - - const [ - { - data: { results, count }, - }, - actionsResponse, - ] = await Promise.all([ - ExecutionEnvironmentsAPI.read( - mergeParams(params, { - ...globallyAvailableParams, - ...organizationIdParams, - }) - ), - ExecutionEnvironmentsAPI.readOptions(), - ]); - return { - executionEnvironments: results, - executionEnvironmentsCount: count, - relatedSearchableKeys: ( - actionsResponse?.data?.related_search_fields || [] - ).map((val) => val.slice(0, -8)), - searchableKeys: getSearchableKeys(actionsResponse.data.actions?.GET), - }; - }, [history.location.search, organizationId]), - { - executionEnvironments: [], - executionEnvironmentsCount: 0, - relatedSearchableKeys: [], - searchableKeys: [], - } - ); - - useEffect(() => { - fetchExecutionEnvironments(); - }, [fetchExecutionEnvironments]); - - if (error) { - return ; - } - if (isLoading) { - return ; - } - - return ( -
- - } - > - { - executionEnvironmentHelpers.setValue([value]); - }} - deselectItem={() => { - executionEnvironmentHelpers.setValue([]); - }} - /> - -
- ); -} -export default AdHocExecutionEnvironmentStep; diff --git a/awx/ui/src/components/AdHocCommands/AdHocPreviewStep.js b/awx/ui/src/components/AdHocCommands/AdHocPreviewStep.js deleted file mode 100644 index 4e1825091318..000000000000 --- a/awx/ui/src/components/AdHocCommands/AdHocPreviewStep.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { t } from '@lingui/macro'; -import { Tooltip } from '@patternfly/react-core'; -import { ExclamationCircleIcon as PFExclamationCircleIcon } from '@patternfly/react-icons'; -import styled from 'styled-components'; -import { VERBOSITY } from '../VerbositySelectField'; -import { toTitleCase } from '../../util/strings'; -import { VariablesDetail } from '../CodeEditor'; -import { jsonToYaml } from '../../util/yaml'; -import { DetailList, Detail } from '../DetailList'; - -const ExclamationCircleIcon = styled(PFExclamationCircleIcon)` - margin-left: 10px; - margin-top: -2px; -`; - -const ErrorMessageWrapper = styled.div` - align-items: center; - color: var(--pf-global--danger-color--200); - display: flex; - font-weight: var(--pf-global--FontWeight--bold); - margin-bottom: 10px; -`; -function AdHocPreviewStep({ hasErrors, values }) { - const { credential, execution_environment, extra_vars, verbosity } = values; - - const items = Object.entries(values); - return ( - <> - {hasErrors && ( - - {t`Some of the previous step(s) have errors`} - - - - - )} - - {items.map( - ([key, value]) => - key !== 'extra_vars' && - key !== 'execution_environment' && - key !== 'credentials' && - key !== 'verbosity' && - !key.startsWith('credential_passwords') && ( - - ) - )} - {credential && ( - - )} - {execution_environment && ( - - )} - {verbosity && ( - - )} - {extra_vars && ( - - )} - - - ); -} - -export default AdHocPreviewStep; diff --git a/awx/ui/src/components/AdHocCommands/index.js b/awx/ui/src/components/AdHocCommands/index.js deleted file mode 100644 index fa981f01e49b..000000000000 --- a/awx/ui/src/components/AdHocCommands/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AdHocCommands'; diff --git a/awx/ui/src/components/AdHocCommands/useAdHocCredentialPasswordStep.js b/awx/ui/src/components/AdHocCommands/useAdHocCredentialPasswordStep.js deleted file mode 100644 index 3f63a3db71a9..000000000000 --- a/awx/ui/src/components/AdHocCommands/useAdHocCredentialPasswordStep.js +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { useFormikContext } from 'formik'; -import { t } from '@lingui/macro'; -import StepName from '../LaunchPrompt/steps/StepName'; -import CredentialPasswordsStep from '../LaunchPrompt/steps/CredentialPasswordsStep'; - -const STEP_ID = 'credentialPasswords'; - -const isValueMissing = (val) => !val || val === ''; - -export default function useCredentialPasswordsStep(showStep, visitedSteps) { - const { values, setFieldError } = useFormikContext(); - const hasError = - showStep && - Object.keys(visitedSteps).includes(STEP_ID) && - checkForError(values); - return { - step: showStep - ? { - id: STEP_ID, - name: ( - - {t`Credential passwords`} - - ), - component: , - enableNext: true, - } - : null, - isReady: true, - contentError: null, - hasError, - setTouched: (setFieldTouched) => { - Object.keys(values.credential_passwords).forEach((credentialValueKey) => - setFieldTouched( - `credential_passwords['${credentialValueKey}']`, - true, - false - ) - ); - }, - validate: () => { - const setPasswordFieldError = (fieldName) => { - setFieldError(fieldName, t`This field may not be blank`); - }; - - Object.entries(values.credentials[0].inputs).forEach(([key, value]) => { - if ( - value === 'ASK' && - isValueMissing( - key === 'password' - ? values.credential_passwords.ssh_password - : values.credential_passwords[key] - ) - ) { - setPasswordFieldError( - key === 'password' - ? `credential_passwords.ssh_password` - : `credential_passwords.${key}` - ); - } - }); - }, - }; -} - -function checkForError(values) { - let hasError = false; - Object.entries(values.credentials[0]?.inputs).forEach(([key, value]) => { - if ( - value === 'ASK' && - isValueMissing( - key === 'password' - ? values.credential_passwords.ssh_password - : values.credential_passwords[key] - ) - ) { - hasError = true; - } - }); - return hasError; -} diff --git a/awx/ui/src/components/AdHocCommands/useAdHocCredentialStep.js b/awx/ui/src/components/AdHocCommands/useAdHocCredentialStep.js deleted file mode 100644 index 1b98ff94eddb..000000000000 --- a/awx/ui/src/components/AdHocCommands/useAdHocCredentialStep.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { useField } from 'formik'; -import { t } from '@lingui/macro'; -import StepName from '../LaunchPrompt/steps/StepName'; -import AdHocCredentialStep from './AdHocCredentialStep'; - -const STEP_ID = 'credentials'; -export default function useAdHocExecutionEnvironmentStep( - visited, - credentialTypeId -) { - const [field, meta, helpers] = useField('credentials'); - const hasError = - Object.keys(visited).includes('credentials') && - !field.value.length && - meta.touched; - - return { - step: { - id: STEP_ID, - key: 3, - name: ( - - {t`Credential`} - - ), - component: , - enableNext: true, - nextButtonText: t`Next`, - }, - hasError, - validate: () => { - if (!meta.value.length) { - helpers.setError('A credential must be selected'); - } - }, - setTouched: (setFieldTouched) => { - setFieldTouched('credentials', true, false); - }, - }; -} diff --git a/awx/ui/src/components/AdHocCommands/useAdHocDetailsStep.js b/awx/ui/src/components/AdHocCommands/useAdHocDetailsStep.js deleted file mode 100644 index d8052817e92a..000000000000 --- a/awx/ui/src/components/AdHocCommands/useAdHocDetailsStep.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { t } from '@lingui/macro'; -import { useFormikContext } from 'formik'; -import StepName from '../LaunchPrompt/steps/StepName'; -import AdHocDetailsStep from './AdHocDetailsStep'; - -const STEP_ID = 'details'; -export default function useAdHocDetailsStep(visited, moduleOptions) { - const { values, touched, setFieldError } = useFormikContext(); - - const hasError = () => { - if (!Object.keys(visited).includes(STEP_ID)) { - return false; - } - if (!values.module_name && touched.module_name) { - return true; - } - - if (values.module_name === 'shell' || values.module_name === 'command') { - if (values.module_args) { - return false; - // eslint-disable-next-line no-else-return - } else { - return true; - } - } - return false; - }; - return { - step: { - id: STEP_ID, - key: 1, - name: ( - - {t`Details`} - - ), - component: , - enableNext: true, - nextButtonText: t`Next`, - }, - hasError: hasError(), - validate: () => { - if (Object.keys(touched).includes('module_name' || 'module_args')) { - if (!values.module_name) { - setFieldError('module_name', t`This field must not be blank.`); - } - if ( - values.module_name === ('command' || 'shell') && - !values.module_args - ) { - setFieldError('module_args', t`This field must not be blank`); - } - } - }, - setTouched: (setFieldTouched) => { - setFieldTouched('module_name', true, false); - setFieldTouched('module_args', true, false); - }, - }; -} diff --git a/awx/ui/src/components/AdHocCommands/useAdHocExecutionEnvironmentStep.js b/awx/ui/src/components/AdHocCommands/useAdHocExecutionEnvironmentStep.js deleted file mode 100644 index 95949bce7d8e..000000000000 --- a/awx/ui/src/components/AdHocCommands/useAdHocExecutionEnvironmentStep.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { t } from '@lingui/macro'; -import StepName from '../LaunchPrompt/steps/StepName'; -import AdHocExecutionEnvironmentStep from './AdHocExecutionEnvironmentStep'; - -const STEP_ID = 'executionEnvironment'; -export default function useAdHocExecutionEnvironmentStep(organizationId) { - return { - step: { - id: STEP_ID, - key: 2, - stepNavItemProps: { style: { whiteSpace: 'nowrap' } }, - name: ( - - {t`Execution Environment`} - - ), - component: ( - - ), - enableNext: true, - nextButtonText: t`Next`, - }, - hasError: false, - validate: () => {}, - setTouched: () => {}, - }; -} diff --git a/awx/ui/src/components/AdHocCommands/useAdHocLaunchSteps.js b/awx/ui/src/components/AdHocCommands/useAdHocLaunchSteps.js deleted file mode 100644 index 038331e23969..000000000000 --- a/awx/ui/src/components/AdHocCommands/useAdHocLaunchSteps.js +++ /dev/null @@ -1,95 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useFormikContext } from 'formik'; -import useCredentialPasswordsStep from './useAdHocCredentialPasswordStep'; -import useAdHocDetailsStep from './useAdHocDetailsStep'; -import useAdHocExecutionEnvironmentStep from './useAdHocExecutionEnvironmentStep'; -import useAdHocCredentialStep from './useAdHocCredentialStep'; -import useAdHocPreviewStep from './useAdHocPreviewStep'; - -function showCredentialPasswordsStep(credential) { - if (!credential?.inputs) { - return false; - } - const { inputs } = credential; - if ( - inputs?.password === 'ASK' || - inputs?.become_password === 'ASK' || - inputs?.ssh_key_unlock === 'ASK' - ) { - return true; - } - - return false; -} - -export default function useAdHocLaunchSteps( - moduleOptions, - organizationId, - credentialTypeId -) { - const { values, resetForm, touched } = useFormikContext(); - - const [visited, setVisited] = useState({}); - const steps = [ - useAdHocDetailsStep(visited, moduleOptions), - useAdHocExecutionEnvironmentStep(organizationId), - useAdHocCredentialStep(visited, credentialTypeId), - useCredentialPasswordsStep( - showCredentialPasswordsStep(values.credentials[0]), - visited - ), - ]; - - useEffect(() => { - const newFormValues = { ...values }; - - if (!values.credentials[0]?.inputs) { - return; - } - if ( - (values.credentials[0].inputs?.password || - values.credentials[0].inputs?.become_password || - values.credentials[0].inputs?.ssh_key_unlock) === 'ASK' - ) - newFormValues.credential_passwords = {}; - Object.keys(values.credentials[0].inputs).forEach((inputKey) => { - if (inputKey === 'become_password' || inputKey === 'ssh_key_unlock') { - newFormValues.credential_passwords[inputKey] = ''; - } - if (inputKey === 'password') { - newFormValues.credential_passwords.ssh_password = ''; - } - }); - resetForm({ - values: newFormValues, - touched, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [values.credentials.length]); - - const hasErrors = steps.some((step) => step.hasError); - - steps.push(useAdHocPreviewStep(hasErrors)); - return { - steps: steps.map((s) => s.step).filter((s) => s != null), - validateStep: (stepId) => - steps.find((s) => s?.step.id === stepId).validate(), - visitStep: (prevStepId, setFieldTouched) => { - setVisited({ - ...visited, - [prevStepId]: true, - }); - steps.find((s) => s?.step?.id === prevStepId).setTouched(setFieldTouched); - }, - visitAllSteps: (setFieldTouched) => { - setVisited({ - details: true, - executionEnvironment: true, - credentials: true, - credentialPasswords: true, - preview: true, - }); - steps.forEach((s) => s.setTouched(setFieldTouched)); - }, - }; -} diff --git a/awx/ui/src/components/AdHocCommands/useAdHocPreviewStep.js b/awx/ui/src/components/AdHocCommands/useAdHocPreviewStep.js deleted file mode 100644 index bc1d9dd17107..000000000000 --- a/awx/ui/src/components/AdHocCommands/useAdHocPreviewStep.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { t } from '@lingui/macro'; -import { useFormikContext } from 'formik'; -import StepName from '../LaunchPrompt/steps/StepName'; -import AdHocPreviewStep from './AdHocPreviewStep'; - -const STEP_ID = 'preview'; -export default function useAdHocPreviewStep(hasErrors) { - const { values } = useFormikContext(); - - return { - step: { - id: STEP_ID, - key: 4, - name: ( - - {t`Preview`} - - ), - component: , - enableNext: !hasErrors, - nextButtonText: t`Launch`, - }, - hasErrors: false, - validate: () => {}, - setTouched: () => {}, - }; -} diff --git a/awx/ui/src/components/AddDropDownButton/AddDropDownButton.js b/awx/ui/src/components/AddDropDownButton/AddDropDownButton.js deleted file mode 100644 index d0e1458c7a67..000000000000 --- a/awx/ui/src/components/AddDropDownButton/AddDropDownButton.js +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable react/jsx-no-useless-fragment */ -import React, { useState, useRef, useEffect } from 'react'; -import { t } from '@lingui/macro'; -import PropTypes from 'prop-types'; -import { Dropdown, DropdownPosition } from '@patternfly/react-core'; -import { useKebabifiedMenu } from 'contexts/Kebabified'; -import { ToolbarAddButton } from '../PaginatedTable'; - -function AddDropDownButton({ dropdownItems, ouiaId }) { - const { isKebabified } = useKebabifiedMenu(); - const [isOpen, setIsOpen] = useState(false); - const element = useRef(null); - - useEffect(() => { - const toggle = (e) => { - if (!isKebabified && (!element || !element.current?.contains(e.target))) { - setIsOpen(false); - } - }; - - document.addEventListener('click', toggle, false); - return () => { - document.removeEventListener('click', toggle); - }; - }, [isKebabified]); - - if (isKebabified) { - return <>{dropdownItems}; - } - - return ( -
- setIsOpen(!isOpen)} - /> - } - dropdownItems={dropdownItems} - ouiaId="add-dropdown" - /> -
- ); -} - -AddDropDownButton.propTypes = { - dropdownItems: PropTypes.arrayOf(PropTypes.element.isRequired).isRequired, -}; - -export { AddDropDownButton as _AddDropDownButton }; -export default AddDropDownButton; diff --git a/awx/ui/src/components/AddDropDownButton/AddDropDownButton.test.js b/awx/ui/src/components/AddDropDownButton/AddDropDownButton.test.js deleted file mode 100644 index d1e16aaa223a..000000000000 --- a/awx/ui/src/components/AddDropDownButton/AddDropDownButton.test.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { DropdownItem } from '@patternfly/react-core'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import AddDropDownButton from './AddDropDownButton'; - -describe('', () => { - const dropdownItems = [ - Add, - Route, - ]; - test('should be closed initially', () => { - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(false); - }); - - test('should render two links', () => { - const wrapper = mountWithContexts( - - ); - wrapper.find('button').simulate('click'); - expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true); - expect(wrapper.find('DropdownItem')).toHaveLength(dropdownItems.length); - }); - - test('should close when button re-clicked', () => { - const wrapper = mountWithContexts( - - ); - wrapper.find('button').simulate('click'); - expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(true); - wrapper.find('button').simulate('click'); - expect(wrapper.find('Dropdown').prop('isOpen')).toEqual(false); - }); -}); diff --git a/awx/ui/src/components/AddDropDownButton/index.js b/awx/ui/src/components/AddDropDownButton/index.js deleted file mode 100644 index 8c87c7df31db..000000000000 --- a/awx/ui/src/components/AddDropDownButton/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AddDropDownButton'; diff --git a/awx/ui/src/components/AddRole/AddResourceRole.js b/awx/ui/src/components/AddRole/AddResourceRole.js deleted file mode 100644 index 570c8e82b9c6..000000000000 --- a/awx/ui/src/components/AddRole/AddResourceRole.js +++ /dev/null @@ -1,286 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { useHistory } from 'react-router-dom'; -import { t } from '@lingui/macro'; -import { TeamsAPI, UsersAPI } from 'api'; -import useSelected from 'hooks/useSelected'; -import SelectableCard from '../SelectableCard'; -import Wizard from '../Wizard'; -import SelectResourceStep from './SelectResourceStep'; -import SelectRoleStep from './SelectRoleStep'; - -const readUsers = async (queryParams) => - UsersAPI.read(Object.assign(queryParams, { is_superuser: false })); - -const readUsersOptions = async () => UsersAPI.readOptions(); - -const readTeams = async (queryParams) => TeamsAPI.read(queryParams); - -const readTeamsOptions = async () => TeamsAPI.readOptions(); - -const userSearchColumns = [ - { - name: t`Username`, - key: 'username__icontains', - isDefault: true, - }, - { - name: t`First Name`, - key: 'first_name__icontains', - }, - { - name: t`Last Name`, - key: 'last_name__icontains', - }, -]; -const userSortColumns = [ - { - name: t`Username`, - key: 'username', - }, - { - name: t`First Name`, - key: 'first_name', - }, - { - name: t`Last Name`, - key: 'last_name', - }, -]; -const teamSearchColumns = [ - { - name: t`Name`, - key: 'name', - isDefault: true, - }, - { - name: t`Created By (Username)`, - key: 'created_by__username', - }, - { - name: t`Modified By (Username)`, - key: 'modified_by__username', - }, -]; - -const teamSortColumns = [ - { - name: t`Name`, - key: 'name', - }, -]; -function AddResourceRole({ onSave, onClose, roles, resource, onError }) { - const history = useHistory(); - - const { - selected: resourcesSelected, - handleSelect: handleResourceSelect, - clearSelected: clearResources, - } = useSelected([]); - const { - selected: rolesSelected, - handleSelect: handleRoleSelect, - clearSelected: clearRoles, - } = useSelected([]); - - const [resourceType, setResourceType] = useState(null); - const [currentStepId, setCurrentStepId] = useState(1); - const [maxEnabledStep, setMaxEnabledStep] = useState(1); - - useEffect(() => { - if (currentStepId === 1 && maxEnabledStep > 1) { - history.push(history.location.pathname); - } - }, [currentStepId, history, maxEnabledStep]); - - const handleResourceTypeSelect = (type) => { - setResourceType(type); - clearResources(); - clearRoles(); - }; - - const handleWizardNext = (step) => { - setCurrentStepId(step.id); - setMaxEnabledStep(step.id); - }; - - const handleWizardGoToStep = (step) => { - setCurrentStepId(step.id); - }; - - const handleWizardSave = async () => { - try { - const roleRequests = []; - - for (let i = 0; i < resourcesSelected.length; i++) { - for (let j = 0; j < rolesSelected.length; j++) { - if (resourceType === 'users') { - roleRequests.push( - UsersAPI.associateRole( - resourcesSelected[i].id, - rolesSelected[j].id - ) - ); - } else if (resourceType === 'teams') { - roleRequests.push( - TeamsAPI.associateRole( - resourcesSelected[i].id, - rolesSelected[j].id - ) - ); - } - } - } - - await Promise.all(roleRequests); - onSave(); - } catch (err) { - onError(err); - onClose(); - } - }; - - // Object roles can be user only, so we remove them when - // showing role choices for team access - const selectableRoles = { ...roles }; - if (resourceType === 'teams') { - Object.keys(roles).forEach((key) => { - if (selectableRoles[key].user_only) { - delete selectableRoles[key]; - } - }); - } - - let wizardTitle = ''; - - switch (resourceType) { - case 'users': - wizardTitle = t`Add User Roles`; - break; - case 'teams': - wizardTitle = t`Add Team Roles`; - break; - default: - wizardTitle = t`Add Roles`; - } - - const steps = [ - { - id: 1, - name: t`Select a Resource Type`, - component: ( -
-
- {t`Choose the type of resource that will be receiving new roles. For example, if you'd like to add new roles to a set of users please choose Users and click Next. You'll be able to select the specific resources in the next step.`} -
- handleResourceTypeSelect('users')} - /> - {resource?.type === 'team' || - (resource?.type === 'credential' && - !resource?.organization) ? null : ( - handleResourceTypeSelect('teams')} - /> - )} -
- ), - nextButtonText: t`Next`, - enableNext: resourceType !== null, - }, - { - id: 2, - name: t`Select Items from List`, - component: ( - <> - {resourceType === 'users' && ( - - )} - {resourceType === 'teams' && ( - - )} - - ), - enableNext: resourcesSelected.length > 0, - nextButtonText: t`Next`, - canJumpTo: maxEnabledStep >= 2, - }, - { - id: 3, - name: t`Select Roles to Apply`, - component: ( - - ), - nextButtonText: t`Save`, - enableNext: rolesSelected.length > 0, - canJumpTo: maxEnabledStep >= 3, - }, - ]; - - const currentStep = steps.find((step) => step.id === currentStepId); - - return ( - setCurrentStepId(step.id)} - onClose={onClose} - onSave={handleWizardSave} - onGoToStep={(step) => handleWizardGoToStep(step)} - steps={steps} - title={wizardTitle} - nextButtonText={currentStep.nextButtonText || undefined} - backButtonText={t`Back`} - cancelButtonText={t`Cancel`} - /> - ); -} - -AddResourceRole.propTypes = { - onClose: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, - roles: PropTypes.shape(), - resource: PropTypes.shape(), -}; - -AddResourceRole.defaultProps = { - roles: {}, - resource: {}, -}; - -export { AddResourceRole as _AddResourceRole }; -export default AddResourceRole; diff --git a/awx/ui/src/components/AddRole/AddResourceRole.test.js b/awx/ui/src/components/AddRole/AddResourceRole.test.js deleted file mode 100644 index 155098ccba53..000000000000 --- a/awx/ui/src/components/AddRole/AddResourceRole.test.js +++ /dev/null @@ -1,426 +0,0 @@ -/* eslint-disable react/jsx-pascal-case */ -import React from 'react'; -import { shallow } from 'enzyme'; -import { createMemoryHistory } from 'history'; -import { act } from 'react-dom/test-utils'; - -import { TeamsAPI, UsersAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AddResourceRole, { _AddResourceRole } from './AddResourceRole'; - -jest.mock('../../api/models/Teams'); -jest.mock('../../api/models/Users'); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useHistory: () => ({ push: jest.fn(), location: { pathname: {} } }), -})); -// TODO: Once error handling is functional in -// this component write tests for it - -describe('<_AddResourceRole />', () => { - const roles = { - admin_role: { - description: 'Can manage all aspects of the organization', - id: 1, - name: 'Admin', - }, - execute_role: { - description: 'May run any executable resources in the organization', - id: 2, - name: 'Execute', - }, - }; - - beforeEach(() => { - UsersAPI.read.mockResolvedValue({ - data: { - count: 2, - results: [ - { id: 1, username: 'foo', url: '' }, - { id: 2, username: 'bar', url: '' }, - { id: 3, username: 'baz', url: '' }, - ], - }, - }); - UsersAPI.readOptions.mockResolvedValue({ - data: { related: {}, actions: { GET: {} } }, - }); - TeamsAPI.read.mockResolvedValue({ - data: { - count: 2, - results: [ - { id: 1, name: 'Team foo', url: '' }, - { id: 2, name: 'Team bar', url: '' }, - ], - }, - }); - TeamsAPI.readOptions.mockResolvedValue({ - data: { related: {}, actions: { GET: {} } }, - }); - }); - - test('initially renders without crashing', () => { - shallow( - <_AddResourceRole - onClose={() => {}} - onSave={() => {}} - roles={roles} - i18n={{ _: (val) => val.toString() }} - /> - ); - }); - test('should save properly', async () => { - let wrapper; - act(() => { - wrapper = mountWithContexts( - {}} onSave={() => {}} roles={roles} />, - { context: { network: { handleHttpError: () => {} } } } - ); - }); - wrapper.update(); - - // Step 1 - const selectableCardWrapper = wrapper.find('SelectableCard'); - expect(selectableCardWrapper.length).toBe(2); - act(() => wrapper.find('SelectableCard[label="Users"]').prop('onClick')()); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // Step 2 - await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0); - expect(wrapper.find('Chip').length).toBe(0); - wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true); - wrapper.find('CheckboxListItem[name="bar"]').invoke('onSelect')(true); - wrapper.find('CheckboxListItem[name="baz"]').invoke('onSelect')(true); - wrapper.find('CheckboxListItem[name="baz"]').invoke('onSelect')(false); - expect( - wrapper.find('CheckboxListItem[name="foo"]').prop('isSelected') - ).toBe(true); - expect( - wrapper.find('CheckboxListItem[name="bar"]').prop('isSelected') - ).toBe(true); - expect( - wrapper.find('CheckboxListItem[name="baz"]').prop('isSelected') - ).toBe(false); - expect(wrapper.find('Chip').length).toBe(2); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - wrapper.update(); - - // Step 3 - act(() => - wrapper.find('Checkbox[aria-label="Admin"]').invoke('onChange')(true) - ); - wrapper.update(); - expect(wrapper.find('Checkbox[aria-label="Admin"]').prop('isChecked')).toBe( - true - ); - - // Save - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - expect(UsersAPI.associateRole).toBeCalledWith(1, 1); - expect(UsersAPI.associateRole).toBeCalledWith(2, 1); - expect(UsersAPI.associateRole).toBeCalledTimes(2); - }); - - test('should call on error properly', async () => { - let wrapper; - const onError = jest.fn(); - UsersAPI.associateRole.mockRejectedValue( - new Error({ - response: { - config: { - method: 'post', - url: '/api/v2/users', - }, - data: 'An error occurred', - status: 403, - }, - }) - ); - act(() => { - wrapper = mountWithContexts( - {}} - onError={onError} - onSave={() => {}} - roles={roles} - />, - { context: { network: { handleHttpError: () => {} } } } - ); - }); - wrapper.update(); - - // Step 1 - const selectableCardWrapper = wrapper.find('SelectableCard'); - expect(selectableCardWrapper.length).toBe(2); - act(() => wrapper.find('SelectableCard[label="Users"]').prop('onClick')()); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // Step 2 - await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0); - act(() => - wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true) - ); - wrapper.update(); - expect( - wrapper.find('CheckboxListItem[name="foo"]').prop('isSelected') - ).toBe(true); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - wrapper.update(); - - // Step 3 - act(() => - wrapper.find('Checkbox[aria-label="Admin"]').invoke('onChange')(true) - ); - wrapper.update(); - expect(wrapper.find('Checkbox[aria-label="Admin"]').prop('isChecked')).toBe( - true - ); - - // Save - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - expect(UsersAPI.associateRole).toBeCalledWith(1, 1); - expect(onError).toBeCalled(); - }); - - test('should update history properly', async () => { - let wrapper; - const history = createMemoryHistory({ - initialEntries: ['organizations/2/access?resource.order_by=-username'], - }); - act(() => { - wrapper = mountWithContexts( - {}} onSave={() => {}} roles={roles} />, - { context: { router: { history } } } - ); - }); - wrapper.update(); - - // Step 1 - const selectableCardWrapper = wrapper.find('SelectableCard'); - expect(selectableCardWrapper.length).toBe(2); - act(() => wrapper.find('SelectableCard[label="Users"]').prop('onClick')()); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // Step 2 - await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0); - act(() => - wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true) - ); - wrapper.update(); - expect( - wrapper.find('CheckboxListItem[name="foo"]').prop('isSelected') - ).toBe(true); - await act(async () => - wrapper.find('PFWizard').prop('onGoToStep')({ id: 1 }) - ); - wrapper.update(); - expect(history.location.pathname).toEqual('organizations/2/access'); - }); - - test('should successfuly click user/team cards', async () => { - let wrapper; - act(() => { - wrapper = mountWithContexts( - {}} onSave={() => {}} roles={roles} />, - { context: { network: { handleHttpError: () => {} } } } - ); - }); - wrapper.update(); - - const selectableCardWrapper = wrapper.find('SelectableCard'); - expect(selectableCardWrapper.length).toBe(2); - act(() => wrapper.find('SelectableCard[label="Users"]').prop('onClick')()); - wrapper.update(); - - await waitForElement( - wrapper, - 'SelectableCard[label="Users"]', - (el) => el.prop('isSelected') === true - ); - act(() => wrapper.find('SelectableCard[label="Teams"]').prop('onClick')()); - wrapper.update(); - - await waitForElement( - wrapper, - 'SelectableCard[label="Teams"]', - (el) => el.prop('isSelected') === true - ); - }); - - test('should reset values with resource type changes', async () => { - let wrapper; - act(() => { - wrapper = mountWithContexts( - {}} onSave={() => {}} roles={roles} />, - { context: { network: { handleHttpError: () => {} } } } - ); - }); - wrapper.update(); - - // Step 1 - const selectableCardWrapper = wrapper.find('SelectableCard'); - expect(selectableCardWrapper.length).toBe(2); - act(() => wrapper.find('SelectableCard[label="Users"]').prop('onClick')()); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // Step 2 - await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0); - act(() => - wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true) - ); - wrapper.update(); - expect( - wrapper.find('CheckboxListItem[name="foo"]').prop('isSelected') - ).toBe(true); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - wrapper.update(); - - // Step 3 - act(() => - wrapper.find('Checkbox[aria-label="Admin"]').invoke('onChange')(true) - ); - wrapper.update(); - expect(wrapper.find('Checkbox[aria-label="Admin"]').prop('isChecked')).toBe( - true - ); - - // Go back to step 1 - act(() => { - wrapper - .find('WizardNavItem[content="Select a Resource Type"]') - .find('button') - .prop('onClick')({ id: 1 }); - }); - wrapper.update(); - expect( - wrapper - .find('WizardNavItem[content="Select a Resource Type"]') - .prop('isCurrent') - ).toBe(true); - - // Go back to step 1 and this time select teams. Doing so should clear following steps - act(() => wrapper.find('SelectableCard[label="Teams"]').prop('onClick')()); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - - // Make sure no teams have been selected - await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0); - wrapper - .find('DataListCheck') - .map((item) => expect(item.prop('checked')).toBe(false)); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - wrapper.update(); - - // Make sure that no roles have been selected - wrapper - .find('Checkbox') - .map((card) => expect(card.prop('isChecked')).toBe(false)); - - // Make sure the save button is disabled - expect(wrapper.find('Button[type="submit"]').prop('isDisabled')).toBe(true); - }); - - test('should not display team as a choice in case credential does not have organization', () => { - const wrapper = mountWithContexts( - {}} - onSave={() => {}} - roles={roles} - resource={{ type: 'credential', organization: null }} - />, - { context: { network: { handleHttpError: () => {} } } } - ); - - expect(wrapper.find('SelectableCard').length).toBe(1); - wrapper.find('SelectableCard[label="Users"]').simulate('click'); - wrapper.update(); - expect( - wrapper.find('SelectableCard[label="Users"]').prop('isSelected') - ).toBe(true); - }); - test('should show correct button text', async () => { - let wrapper; - act(() => { - wrapper = mountWithContexts( - {}} onSave={() => {}} roles={roles} />, - { context: { network: { handleHttpError: () => {} } } } - ); - }); - wrapper.update(); - - // Step 1 - const selectableCardWrapper = wrapper.find('SelectableCard'); - expect(selectableCardWrapper.length).toBe(2); - act(() => wrapper.find('SelectableCard[label="Users"]').prop('onClick')()); - wrapper.update(); - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - expect(wrapper.find('Button[type="submit"]').text()).toBe('Next'); - - wrapper.update(); - - // Step 2 - await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0); - expect(wrapper.find('Chip').length).toBe(0); - act(() => - wrapper.find('CheckboxListItem[name="foo"]').invoke('onSelect')(true) - ); - wrapper.update(); - expect( - wrapper.find('CheckboxListItem[name="foo"]').prop('isSelected') - ).toBe(true); - expect(wrapper.find('Chip').length).toBe(1); - expect(wrapper.find('Button[type="submit"]').text()).toBe('Next'); - act(() => wrapper.find('Button[type="submit"]').prop('onClick')()); - wrapper.update(); - - // Step 3 - act(() => - wrapper.find('Checkbox[aria-label="Admin"]').invoke('onChange')(true) - ); - expect(wrapper.find('Button[type="submit"]').text()).toBe('Save'); - wrapper.update(); - - // Go Back - await act(async () => - wrapper.find('Button[variant="secondary"]').prop('onClick')() - ); - wrapper.update(); - expect(wrapper.find('Button[type="submit"]').text()).toBe('Next'); - - // return to last step - await act(async () => - wrapper.find('Button[type="submit"]').prop('onClick')() - ); - wrapper.update(); - expect(wrapper.find('Button[type="submit"]').text()).toBe('Save'); - }); -}); diff --git a/awx/ui/src/components/AddRole/CheckboxCard.js b/awx/ui/src/components/AddRole/CheckboxCard.js deleted file mode 100644 index b055ee93099b..000000000000 --- a/awx/ui/src/components/AddRole/CheckboxCard.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Checkbox as PFCheckbox } from '@patternfly/react-core'; -import styled from 'styled-components'; - -const CheckboxWrapper = styled.div` - display: flex; - border: 1px solid var(--pf-global--BorderColor--200); - border-radius: var(--pf-global--BorderRadius--sm); - padding: 10px; -`; - -const Checkbox = styled(PFCheckbox)` - width: 100%; - & label { - width: 100%; - } -`; - -function CheckboxCard(props) { - const { name, description, isSelected, onSelect, itemId } = props; - return ( - - -
{name}
-
{description}
- - } - value={itemId} - /> -
- ); -} - -CheckboxCard.propTypes = { - name: PropTypes.string.isRequired, - description: PropTypes.string, - isSelected: PropTypes.bool, - onSelect: PropTypes.func, - itemId: PropTypes.number.isRequired, -}; - -CheckboxCard.defaultProps = { - description: '', - isSelected: false, - onSelect: null, -}; - -export default CheckboxCard; diff --git a/awx/ui/src/components/AddRole/CheckboxCard.test.js b/awx/ui/src/components/AddRole/CheckboxCard.test.js deleted file mode 100644 index 637cbac403eb..000000000000 --- a/awx/ui/src/components/AddRole/CheckboxCard.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { shallow } from 'enzyme'; -import CheckboxCard from './CheckboxCard'; - -describe('', () => { - let wrapper; - test('initially renders without crashing', () => { - wrapper = shallow(); - expect(wrapper.length).toBe(1); - }); -}); diff --git a/awx/ui/src/components/AddRole/SelectResourceStep.js b/awx/ui/src/components/AddRole/SelectResourceStep.js deleted file mode 100644 index b0e8897320c6..000000000000 --- a/awx/ui/src/components/AddRole/SelectResourceStep.js +++ /dev/null @@ -1,153 +0,0 @@ -import React, { useCallback, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { useLocation } from 'react-router-dom'; -import { t } from '@lingui/macro'; -import useRequest from 'hooks/useRequest'; -import { SearchColumns, SortColumns } from 'types'; -import { getQSConfig, parseQueryString } from 'util/qs'; -import DataListToolbar from '../DataListToolbar'; -import CheckboxListItem from '../CheckboxListItem'; -import { SelectedList } from '../SelectedList'; -import PaginatedTable, { - HeaderCell, - HeaderRow, - getSearchableKeys, -} from '../PaginatedTable'; - -const QS_Config = (sortColumns) => - getQSConfig('resource', { - page: 1, - page_size: 5, - order_by: `${ - sortColumns.filter((col) => col.key === 'name').length - ? 'name' - : 'username' - }`, - }); -function SelectResourceStep({ - searchColumns, - sortColumns, - displayKey, - onRowClick, - selectedLabel, - selectedResourceRows, - fetchItems, - fetchOptions, -}) { - const location = useLocation(); - - const { - isLoading, - error, - request: readResourceList, - result: { resources, itemCount, relatedSearchableKeys, searchableKeys }, - } = useRequest( - useCallback(async () => { - const queryParams = parseQueryString( - QS_Config(sortColumns), - location.search - ); - - const [ - { - data: { count, results }, - }, - actionsResponse, - ] = await Promise.all([fetchItems(queryParams), fetchOptions()]); - return { - resources: results, - itemCount: count, - relatedSearchableKeys: ( - actionsResponse?.data?.related_search_fields || [] - ).map((val) => val.slice(0, -8)), - searchableKeys: getSearchableKeys(actionsResponse.data.actions?.GET), - }; - }, [location, fetchItems, fetchOptions, sortColumns]), - { - resources: [], - itemCount: 0, - relatedSearchableKeys: [], - searchableKeys: [], - } - ); - - useEffect(() => { - readResourceList(); - }, [readResourceList]); - - return ( - <> -
- {t`Choose the resources that will be receiving new roles. You'll be able to select the roles to apply in the next step. Note that the resources chosen here will receive all roles chosen in the next step.`} -
- {selectedResourceRows.length > 0 && ( - - )} - - - {sortColumns.map(({ name, key }) => ( - - {name} - - ))} - - } - renderRow={(item, index) => ( - i.id === item.id)} - itemId={item.id} - item={item} - rowIndex={index} - key={item.id} - columns={sortColumns} - name={item[displayKey]} - label={item[displayKey]} - onSelect={() => onRowClick(item)} - onDeselect={() => onRowClick(item)} - /> - )} - renderToolbar={(props) => } - showPageSizeOptions={false} - /> - - ); -} - -SelectResourceStep.propTypes = { - searchColumns: SearchColumns, - sortColumns: SortColumns, - displayKey: PropTypes.string, - onRowClick: PropTypes.func, - fetchItems: PropTypes.func.isRequired, - selectedLabel: PropTypes.string, - selectedResourceRows: PropTypes.arrayOf(PropTypes.object), -}; - -SelectResourceStep.defaultProps = { - searchColumns: null, - sortColumns: null, - displayKey: 'name', - onRowClick: () => {}, - selectedLabel: null, - selectedResourceRows: [], -}; - -export { SelectResourceStep as _SelectResourceStep }; -export default SelectResourceStep; diff --git a/awx/ui/src/components/AddRole/SelectResourceStep.test.js b/awx/ui/src/components/AddRole/SelectResourceStep.test.js deleted file mode 100644 index 0654558a17fe..000000000000 --- a/awx/ui/src/components/AddRole/SelectResourceStep.test.js +++ /dev/null @@ -1,126 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; - -import { - mountWithContexts, - shallowWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import SelectResourceStep from './SelectResourceStep'; - -describe('', () => { - const searchColumns = [ - { - name: 'Username', - key: 'username__icontains', - isDefault: true, - }, - ]; - - const sortColumns = [ - { - name: 'Username', - key: 'username', - }, - ]; - afterEach(() => { - jest.restoreAllMocks(); - }); - test('initially renders without crashing', async () => { - act(() => { - shallowWithContexts( - {}} - fetchItems={() => {}} - fetchOptions={() => {}} - /> - ); - }); - }); - - test('fetches resources on mount and adds items to list', async () => { - const handleSearch = jest.fn().mockResolvedValue({ - data: { - count: 2, - results: [ - { id: 1, username: 'foo', url: 'item/1' }, - { id: 2, username: 'bar', url: 'item/2' }, - ], - }, - }); - const options = jest.fn().mockResolvedValue({ - data: { - actions: { - GET: {}, - POST: {}, - }, - related_search_fields: [], - }, - }); - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - {}} - fetchItems={handleSearch} - fetchOptions={options} - /> - ); - }); - expect(handleSearch).toHaveBeenCalledWith({ - order_by: 'username', - page: 1, - page_size: 5, - }); - waitForElement(wrapper, 'CheckBoxListItem', (el) => el.length === 2); - }); - - test('clicking on row fires callback with correct params', async () => { - const handleRowClick = jest.fn(); - const data = { - count: 2, - results: [ - { id: 1, username: 'foo', url: 'item/1' }, - { id: 2, username: 'bar', url: 'item/2' }, - ], - }; - const options = jest.fn().mockResolvedValue({ - data: { - actions: { - GET: {}, - POST: {}, - }, - related_search_fields: [], - }, - }); - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - ({ data })} - fetchOptions={options} - selectedResourceRows={[]} - /> - ); - }); - wrapper.update(); - const checkboxListItemWrapper = wrapper.find('CheckboxListItem'); - expect(checkboxListItemWrapper.length).toBe(2); - - checkboxListItemWrapper - .first() - .find('input[type="checkbox"]') - .simulate('click'); - expect(handleRowClick).toHaveBeenCalledWith(data.results[0]); - }); -}); diff --git a/awx/ui/src/components/AddRole/SelectRoleStep.js b/awx/ui/src/components/AddRole/SelectRoleStep.js deleted file mode 100644 index e235d4661a35..000000000000 --- a/awx/ui/src/components/AddRole/SelectRoleStep.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; - -import CheckboxCard from './CheckboxCard'; -import { SelectedList } from '../SelectedList'; - -function RolesStep({ - onRolesClick, - roles, - selectedListKey, - selectedListLabel, - selectedResourceRows, - selectedRoleRows, -}) { - return ( - <> -
- {t`Choose roles to apply to the selected resources. Note that all selected roles will be applied to all selected resources.`} -
-
- {selectedResourceRows.length > 0 && ( - - )} -
-
- {Object.keys(roles).map((role) => ( - item.id === roles[role].id - )} - key={roles[role].id} - name={roles[role].name} - onSelect={() => onRolesClick(roles[role])} - /> - ))} -
- - ); -} - -RolesStep.propTypes = { - onRolesClick: PropTypes.func, - roles: PropTypes.objectOf(PropTypes.object).isRequired, - selectedListKey: PropTypes.string, - selectedListLabel: PropTypes.string, - selectedResourceRows: PropTypes.arrayOf(PropTypes.object), - selectedRoleRows: PropTypes.arrayOf(PropTypes.object), -}; - -RolesStep.defaultProps = { - onRolesClick: () => {}, - selectedListKey: 'name', - selectedListLabel: null, - selectedResourceRows: [], - selectedRoleRows: [], -}; - -export default RolesStep; diff --git a/awx/ui/src/components/AddRole/SelectRoleStep.test.js b/awx/ui/src/components/AddRole/SelectRoleStep.test.js deleted file mode 100644 index c871db337e70..000000000000 --- a/awx/ui/src/components/AddRole/SelectRoleStep.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; - -import { - mountWithContexts, - shallowWithContexts, -} from '../../../testUtils/enzymeHelpers'; - -import SelectRoleStep from './SelectRoleStep'; - -describe('', () => { - let wrapper; - const roles = { - project_admin_role: { - id: 1, - name: 'Project Admin', - description: 'Can manage all projects of the organization', - }, - execute_role: { - id: 2, - name: 'Execute', - description: 'May run any executable resources in the organization', - }, - }; - const selectedRoles = [ - { - id: 1, - name: 'Project Admin', - description: 'Can manage all projects of the organization', - }, - ]; - const selectedResourceRows = [ - { - id: 1, - name: 'foo', - }, - ]; - - test('initially renders without crashing', () => { - wrapper = shallowWithContexts( - - ); - expect(wrapper.length).toBe(1); - }); - - test('clicking role fires onRolesClick callback', () => { - const onRolesClick = jest.fn(); - wrapper = mountWithContexts( - - ); - const CheckboxCards = wrapper.find('CheckboxCard'); - expect(CheckboxCards.length).toBe(2); - CheckboxCards.first().prop('onSelect')(); - expect(onRolesClick).toBeCalledWith({ - id: 1, - name: 'Project Admin', - description: 'Can manage all projects of the organization', - }); - }); -}); diff --git a/awx/ui/src/components/AddRole/index.js b/awx/ui/src/components/AddRole/index.js deleted file mode 100644 index 52e9ec78d470..000000000000 --- a/awx/ui/src/components/AddRole/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { default as AddResourceRole } from './AddResourceRole'; -export { default as CheckboxCard } from './CheckboxCard'; -export { default as SelectResourceStep } from './SelectResourceStep'; -export { default as SelectRoleStep } from './SelectRoleStep'; diff --git a/awx/ui/src/components/AlertModal/AlertModal.js b/awx/ui/src/components/AlertModal/AlertModal.js deleted file mode 100644 index df0afb87aa22..000000000000 --- a/awx/ui/src/components/AlertModal/AlertModal.js +++ /dev/null @@ -1,88 +0,0 @@ -import 'styled-components/macro'; -import React from 'react'; -import { Modal, Title } from '@patternfly/react-core'; -import { - CheckCircleIcon, - ExclamationCircleIcon, - ExclamationTriangleIcon, - InfoCircleIcon, - TimesCircleIcon, -} from '@patternfly/react-icons'; - -import { t } from '@lingui/macro'; -import styled from 'styled-components'; - -const Header = styled.div` - display: flex; - svg { - margin-right: 16px; - } -`; - -function AlertModal({ - isOpen = null, - title, - label, - variant, - children, - ...props -}) { - const variantIcons = { - danger: ( - - ), - error: ( - - ), - info: ( - - ), - success: ( - - ), - warning: ( - - ), - }; - - const customHeader = ( -
- {variant ? variantIcons[variant] : null} - - {title} - -
- ); - - return ( - - {children} - - ); -} - -export default AlertModal; diff --git a/awx/ui/src/components/AlertModal/AlertModal.test.js b/awx/ui/src/components/AlertModal/AlertModal.test.js deleted file mode 100644 index 0173e5a378f9..000000000000 --- a/awx/ui/src/components/AlertModal/AlertModal.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import AlertModal from './AlertModal'; - -describe('AlertModal', () => { - test('renders the expected content', () => { - const wrapper = mountWithContexts( - Are you sure? - ); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/AlertModal/index.js b/awx/ui/src/components/AlertModal/index.js deleted file mode 100644 index f40ad70d3d06..000000000000 --- a/awx/ui/src/components/AlertModal/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AlertModal'; diff --git a/awx/ui/src/components/AnsibleSelect/AnsibleSelect.js b/awx/ui/src/components/AnsibleSelect/AnsibleSelect.js deleted file mode 100644 index ff1c8395ee46..000000000000 --- a/awx/ui/src/components/AnsibleSelect/AnsibleSelect.js +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import { - arrayOf, - oneOfType, - func, - number, - string, - shape, - bool, -} from 'prop-types'; - -import { t } from '@lingui/macro'; -import { FormSelect, FormSelectOption } from '@patternfly/react-core'; - -function AnsibleSelect({ - id, - data, - isValid, - onBlur, - value, - className, - isDisabled, - onChange, - name, -}) { - const onSelectChange = (val, event) => { - event.target.name = name; - onChange(event, val); - }; - - return ( - - {data.map((option) => ( - - {option.label} - - ))} - - ); -} - -const Option = shape({ - key: oneOfType([string, number]).isRequired, - value: oneOfType([string, number]).isRequired, - label: string.isRequired, - isDisabled: bool, -}); - -AnsibleSelect.defaultProps = { - data: [], - isValid: true, - onBlur: () => {}, - className: '', - isDisabled: false, -}; - -AnsibleSelect.propTypes = { - data: arrayOf(Option), - id: string.isRequired, - isValid: bool, - onBlur: func, - onChange: func.isRequired, - value: oneOfType([string, number]).isRequired, - className: string, - isDisabled: bool, -}; - -export { AnsibleSelect as _AnsibleSelect }; -export default AnsibleSelect; diff --git a/awx/ui/src/components/AnsibleSelect/AnsibleSelect.test.js b/awx/ui/src/components/AnsibleSelect/AnsibleSelect.test.js deleted file mode 100644 index f0e2416af41f..000000000000 --- a/awx/ui/src/components/AnsibleSelect/AnsibleSelect.test.js +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import AnsibleSelect from './AnsibleSelect'; - -const mockData = [ - { - key: 'baz', - label: 'Baz', - value: '/var/lib/awx/venv/baz/', - }, - { - key: 'default', - label: 'Default', - value: '/var/lib/awx/venv/ansible/', - }, -]; - -describe('', () => { - const onChange = jest.fn(); - test('initially renders successfully', async () => { - mountWithContexts( - {}} - data={mockData} - /> - ); - }); - - test('calls "onSelectChange" on dropdown select change', () => { - const wrapper = mountWithContexts( - - ); - expect(onChange).not.toHaveBeenCalled(); - wrapper.find('select').simulate('change'); - expect(onChange).toHaveBeenCalled(); - }); - - test('Returns correct select options', () => { - const wrapper = mountWithContexts( - {}} - data={mockData} - /> - ); - - expect(wrapper.find('FormSelect')).toHaveLength(1); - expect(wrapper.find('FormSelectOption')).toHaveLength(2); - }); -}); diff --git a/awx/ui/src/components/AnsibleSelect/index.js b/awx/ui/src/components/AnsibleSelect/index.js deleted file mode 100644 index 647f195bbdb7..000000000000 --- a/awx/ui/src/components/AnsibleSelect/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AnsibleSelect'; diff --git a/awx/ui/src/components/AppContainer/AppContainer.js b/awx/ui/src/components/AppContainer/AppContainer.js deleted file mode 100644 index f3c07596a6b4..000000000000 --- a/awx/ui/src/components/AppContainer/AppContainer.js +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { withRouter } from 'react-router-dom'; -import { - Button, - Nav, - NavList, - Page, - PageHeader as PFPageHeader, - PageHeaderTools, - PageHeaderToolsGroup, - PageHeaderToolsItem, - PageSidebar, -} from '@patternfly/react-core'; -import { t, Plural } from '@lingui/macro'; - -import styled from 'styled-components'; - -import { useConfig, useAuthorizedPath } from 'contexts/Config'; -import { useSession } from 'contexts/Session'; -import issuePendoIdentity from 'util/issuePendoIdentity'; -import About from '../About'; -import BrandLogo from './BrandLogo'; -import NavExpandableGroup from './NavExpandableGroup'; -import PageHeaderToolbar from './PageHeaderToolbar'; -import AlertModal from '../AlertModal'; - -const PageHeader = styled(PFPageHeader)` - & .pf-c-page__header-brand-link { - color: inherit; - &:hover { - color: inherit; - } - } -`; - -function AppContainer({ navRouteConfig = [], children }) { - const config = useConfig(); - const { logout, handleSessionContinue, sessionCountdown } = useSession(); - - const isReady = !!config.license_info; - const isSidebarVisible = useAuthorizedPath(); - const [isAboutModalOpen, setIsAboutModalOpen] = useState(false); - - const handleAboutModalOpen = () => setIsAboutModalOpen(true); - const handleAboutModalClose = () => setIsAboutModalOpen(false); - - useEffect(() => { - if ('analytics_status' in config) { - issuePendoIdentity(config); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [config.analytics_status]); - - const brandName = config?.license_info?.product_name; - const alt = brandName ? t`${brandName} logo` : t`brand logo`; - - const header = ( - } - logoProps={{ href: '/' }} - headerTools={ - - } - /> - ); - - const simpleHeader = config.isLoading ? null : ( - } - headerTools={ - - - - - - - - } - /> - ); - - const sidebar = ( - - - {navRouteConfig.map(({ groupId, groupTitle, routes }) => ( - - ))} - - - } - /> - ); - - return ( - <> - - {isReady ? children : null} - - - 0} - onClose={logout} - showClose={false} - variant="warning" - actions={[ - , - , - ]} - > - - - - ); -} - -export { AppContainer as _AppContainer }; -export default withRouter(AppContainer); diff --git a/awx/ui/src/components/AppContainer/AppContainer.test.js b/awx/ui/src/components/AppContainer/AppContainer.test.js deleted file mode 100644 index 7c5a9ee87b2a..000000000000 --- a/awx/ui/src/components/AppContainer/AppContainer.test.js +++ /dev/null @@ -1,204 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { MeAPI, RootAPI } from 'api'; -import { useAuthorizedPath } from 'contexts/Config'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AppContainer from './AppContainer'; - -jest.mock('../../api'); -jest.mock('../../util/bootstrapPendo'); - -global.pendo = { - initialize: jest.fn(), -}; - -describe('', () => { - const version = '222'; - - beforeEach(() => { - RootAPI.readAssetVariables.mockResolvedValue({ - data: { - BRAND_NAME: 'AWX', - PENDO_API_KEY: 'some-pendo-key', - }, - }); - MeAPI.read.mockResolvedValue({ data: { results: [{}] } }); - useAuthorizedPath.mockImplementation(() => true); - }); - - afterEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - }); - - test('expected content is rendered', async () => { - const routeConfig = [ - { - groupTitle: Group One, - groupId: 'group_one', - routes: [ - { title: 'Foo', path: '/foo' }, - { title: 'Bar', path: '/bar' }, - ], - }, - { - groupTitle: Group Two, - groupId: 'group_two', - routes: [{ title: 'Fiz', path: '/fiz' }], - }, - ]; - - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - - {routeConfig.map(({ groupId }) => ( -
- ))} - , - { - context: { - config: { - analytics_status: 'detailed', - ansible_version: null, - custom_virtualenvs: [], - version: '9000', - me: { is_superuser: true }, - toJSON: () => '/config/', - license_info: { - valid_key: true, - }, - }, - }, - } - ); - }); - wrapper.update(); - - // page components - expect(wrapper.length).toBe(1); - expect(wrapper.find('PageHeader').length).toBe(1); - expect(wrapper.find('PageSidebar').length).toBe(1); - - // sidebar groups and route links - expect(wrapper.find('NavExpandableGroup').length).toBe(2); - expect(wrapper.find('a[href="/foo"]').length).toBe(1); - expect(wrapper.find('a[href="/bar"]').length).toBe(1); - expect(wrapper.find('a[href="/fiz"]').length).toBe(1); - - expect(wrapper.find('#group_one').length).toBe(1); - expect(wrapper.find('#group_two').length).toBe(1); - - expect(global.pendo.initialize).toHaveBeenCalledTimes(1); - }); - - test('Pendo not initialized when key is missing', async () => { - RootAPI.readAssetVariables.mockResolvedValue({ - data: { - BRAND_NAME: 'AWX', - PENDO_API_KEY: '', - }, - }); - let wrapper; - await act(async () => { - wrapper = mountWithContexts(, { - context: { - config: { - analytics_status: 'detailed', - ansible_version: null, - custom_virtualenvs: [], - version: '9000', - me: { is_superuser: true }, - toJSON: () => '/config/', - license_info: { - valid_key: true, - }, - }, - }, - }); - }); - wrapper.update(); - expect(global.pendo.initialize).toHaveBeenCalledTimes(0); - }); - - test('Pendo not initialized when status is analytics off', async () => { - let wrapper; - await act(async () => { - wrapper = mountWithContexts(, { - context: { - config: { - analytics_status: 'off', - ansible_version: null, - custom_virtualenvs: [], - version: '9000', - me: { is_superuser: true }, - toJSON: () => '/config/', - license_info: { - valid_key: true, - }, - }, - }, - }); - }); - wrapper.update(); - expect(global.pendo.initialize).toHaveBeenCalledTimes(0); - }); - - test('opening the about modal renders prefetched config data', async () => { - const aboutDropdown = 'Dropdown QuestionCircleIcon'; - const aboutButton = 'DropdownItem li button'; - const aboutModalContent = 'AboutModalBoxContent'; - const aboutModalClose = 'button[aria-label="Close Dialog"]'; - - let wrapper; - await act(async () => { - wrapper = mountWithContexts(, { - context: { config: { version } }, - }); - }); - - // open about dropdown menu - await waitForElement(wrapper, aboutDropdown); - wrapper.find(aboutDropdown).simulate('click'); - - // open about modal - ( - await waitForElement(wrapper, aboutButton, (el) => !el.props().disabled) - ).simulate('click'); - - // check about modal content - const content = await waitForElement(wrapper, aboutModalContent); - expect(content.find('pre').text()).toContain(`< AWX ${version} >`); - - // close about modal - wrapper.find(aboutModalClose).simulate('click'); - expect(wrapper.find(aboutModalContent)).toHaveLength(0); - }); - - test('logout makes expected call to api client', async () => { - const userMenuButton = 'UserIcon'; - const logoutButton = '#logout-button button'; - const logout = jest.fn(); - let wrapper; - await act(async () => { - wrapper = mountWithContexts(, { - context: { - session: { - logout, - }, - }, - }); - }); - // open the user menu - expect(wrapper.find(logoutButton)).toHaveLength(0); - wrapper.find(userMenuButton).simulate('click'); - expect(wrapper.find(logoutButton)).toHaveLength(1); - - // logout - wrapper.find(logoutButton).simulate('click'); - expect(logout).toHaveBeenCalledTimes(1); - }); -}); diff --git a/awx/ui/src/components/AppContainer/BrandLogo.js b/awx/ui/src/components/AppContainer/BrandLogo.js deleted file mode 100644 index b3bb8efdfb69..000000000000 --- a/awx/ui/src/components/AppContainer/BrandLogo.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import styled from 'styled-components'; - -const BrandImg = styled.img` - flex: initial; - height: 76px; - width: initial; - padding-left: 0px; - margin: 0px 0px 0px 0px; - max-width: 100px; - max-height: initial; - pointer-events: none; -`; - -const BrandLogo = ({ alt }) => ( - -); - -export default BrandLogo; diff --git a/awx/ui/src/components/AppContainer/BrandLogo.test.js b/awx/ui/src/components/AppContainer/BrandLogo.test.js deleted file mode 100644 index 641332be6663..000000000000 --- a/awx/ui/src/components/AppContainer/BrandLogo.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import BrandLogo from './BrandLogo'; - -let logoWrapper; -let brandLogoElem; -let imgElem; - -const findChildren = () => { - brandLogoElem = logoWrapper.find('BrandLogo'); - imgElem = logoWrapper.find('img'); -}; - -describe('', () => { - test('initially renders without crashing', () => { - logoWrapper = mountWithContexts(); - findChildren(); - expect(logoWrapper.length).toBe(1); - expect(brandLogoElem.length).toBe(1); - expect(imgElem.length).toBe(1); - }); -}); diff --git a/awx/ui/src/components/AppContainer/NavExpandableGroup.js b/awx/ui/src/components/AppContainer/NavExpandableGroup.js deleted file mode 100644 index a5c162170829..000000000000 --- a/awx/ui/src/components/AppContainer/NavExpandableGroup.js +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import PropTypes, { oneOfType, string, arrayOf } from 'prop-types'; -import { matchPath, Link, useHistory } from 'react-router-dom'; -import { NavExpandable, NavItem } from '@patternfly/react-core'; - -function NavExpandableGroup(props) { - const history = useHistory(); - const { groupId, groupTitle, routes } = props; - - // Extract a list of paths from the route params and store them for later. This creates - // an array of url paths associated with any NavItem component rendered by this component. - const navItemPaths = routes.map(({ path }) => path); - - const isActive = navItemPaths.some(isActivePath); - - function isActivePath(path) { - return Boolean(matchPath(history.location.pathname, { path })); - } - - if (routes.length === 1 && groupId === 'settings') { - const [{ path }] = routes; - return ( - - {groupTitle} - - ); - } - - return ( - - {routes.map(({ path, title }) => ( - - {title} - - ))} - - ); -} - -NavExpandableGroup.propTypes = { - groupId: string.isRequired, - groupTitle: oneOfType([PropTypes.element, string]).isRequired, - routes: arrayOf(PropTypes.object).isRequired, -}; - -export default NavExpandableGroup; diff --git a/awx/ui/src/components/AppContainer/NavExpandableGroup.test.js b/awx/ui/src/components/AppContainer/NavExpandableGroup.test.js deleted file mode 100644 index 90c0214ac6ad..000000000000 --- a/awx/ui/src/components/AppContainer/NavExpandableGroup.test.js +++ /dev/null @@ -1,110 +0,0 @@ -import React from 'react'; -import { MemoryRouter, withRouter } from 'react-router-dom'; -import { mount } from 'enzyme'; - -import { Nav } from '@patternfly/react-core'; -import _NavExpandableGroup from './NavExpandableGroup'; - -const NavExpandableGroup = withRouter(_NavExpandableGroup); - -describe('NavExpandableGroup', () => { - test('initialization and render', () => { - const component = mount( - - - - ).find('NavExpandableGroup'); - - expect(component.find('NavItem').length).toEqual(3); - let link = component.find('NavItem').at(0); - expect(component.find('NavItem').at(0).prop('isActive')).toBeTruthy(); - expect(link.find('Link').prop('to')).toBe('/foo'); - - link = component.find('NavItem').at(1); - expect(link.prop('isActive')).toBeFalsy(); - expect(link.find('Link').prop('to')).toBe('/bar'); - - link = component.find('NavItem').at(2); - expect(link.prop('isActive')).toBeFalsy(); - expect(link.find('Link').prop('to')).toBe('/fiz'); - }); - - test('when location is /foo/1/bar/fiz isActive returns false', () => { - const component = mount( - - - - ).find('NavExpandableGroup'); - - expect(component.find('NavItem').length).toEqual(3); - const link = component.find('NavItem').at(0); - expect(component.find('NavItem').at(0).prop('isActive')).toBeTruthy(); - expect(link.find('Link').prop('to')).toBe('/foo'); - }); - - test('when location is /fo isActive returns false', () => { - const component = mount( - - - - ).find('NavExpandableGroup'); - - expect(component.find('NavItem').length).toEqual(3); - const link = component.find('NavItem').at(0); - expect(component.find('NavItem').at(0).prop('isActive')).toBeFalsy(); - expect(link.find('Link').prop('to')).toBe('/foo'); - }); - - test('when location is /foo isActive returns true', () => { - const component = mount( - - - - ).find('NavExpandableGroup'); - - expect(component.find('NavItem').length).toEqual(3); - const link = component.find('NavItem').at(0); - expect(component.find('NavItem').at(0).prop('isActive')).toBeTruthy(); - expect(link.find('Link').prop('to')).toBe('/foo'); - }); -}); diff --git a/awx/ui/src/components/AppContainer/PageHeaderToolbar.js b/awx/ui/src/components/AppContainer/PageHeaderToolbar.js deleted file mode 100644 index 2d87d596a5a2..000000000000 --- a/awx/ui/src/components/AppContainer/PageHeaderToolbar.js +++ /dev/null @@ -1,189 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; -import { - Dropdown, - DropdownItem, - DropdownToggle, - DropdownPosition, - NotificationBadge, - NotificationBadgeVariant, - PageHeaderTools, - PageHeaderToolsGroup, - PageHeaderToolsItem, - Tooltip, -} from '@patternfly/react-core'; -import { QuestionCircleIcon, UserIcon } from '@patternfly/react-icons'; -import { WorkflowApprovalsAPI } from 'api'; -import useRequest from 'hooks/useRequest'; -import getDocsBaseUrl from 'util/getDocsBaseUrl'; -import { useConfig } from 'contexts/Config'; -import useWsPendingApprovalCount from './useWsPendingApprovalCount'; - -const PendingWorkflowApprovals = styled.div` - display: flex; - align-items: center; - padding: 10px; - margin-right: 10px; -`; - -function PageHeaderToolbar({ - isAboutDisabled, - onAboutClick, - onLogoutClick, - loggedInUser, -}) { - const [isHelpOpen, setIsHelpOpen] = useState(false); - const [isUserOpen, setIsUserOpen] = useState(false); - const config = useConfig(); - - const { request: fetchPendingApprovalCount, result: pendingApprovals } = - useRequest( - useCallback(async () => { - const { - data: { count }, - } = await WorkflowApprovalsAPI.read({ - status: 'pending', - page_size: 1, - }); - return count; - }, []), - 0 - ); - - const pendingApprovalsCount = useWsPendingApprovalCount( - pendingApprovals, - fetchPendingApprovalCount - ); - - useEffect(() => { - fetchPendingApprovalCount(); - }, [fetchPendingApprovalCount]); - - const handleHelpSelect = () => { - setIsHelpOpen(!isHelpOpen); - }; - - const handleUserSelect = () => { - setIsUserOpen(!isUserOpen); - }; - return ( - - - - - - - - - - - - - - - - } - dropdownItems={[ - - {t`Help`} - , - - {t`About`} - , - ]} - /> - - - - - {loggedInUser && ( - - {loggedInUser.username} - - )} - - } - dropdownItems={[ - - {t`User Details`} - , - - {t`Logout`} - , - ]} - /> - - - - ); -} - -PageHeaderToolbar.propTypes = { - isAboutDisabled: PropTypes.bool, - onAboutClick: PropTypes.func.isRequired, - onLogoutClick: PropTypes.func.isRequired, -}; - -PageHeaderToolbar.defaultProps = { - isAboutDisabled: false, -}; - -export default PageHeaderToolbar; diff --git a/awx/ui/src/components/AppContainer/PageHeaderToolbar.test.js b/awx/ui/src/components/AppContainer/PageHeaderToolbar.test.js deleted file mode 100644 index 1fd1734c5925..000000000000 --- a/awx/ui/src/components/AppContainer/PageHeaderToolbar.test.js +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { WorkflowApprovalsAPI } from 'api'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import PageHeaderToolbar from './PageHeaderToolbar'; - -jest.mock('../../api'); - -let wrapper; - -describe('PageHeaderToolbar', () => { - const pageHelpDropdownSelector = 'Dropdown QuestionCircleIcon'; - const pageUserDropdownSelector = 'Dropdown UserIcon'; - const onAboutClick = jest.fn(); - const onLogoutClick = jest.fn(); - - test('expected content is rendered on initialization', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - - expect( - wrapper.find( - 'Link[to="/workflow_approvals?workflow_approvals.status=pending"]' - ) - ).toHaveLength(1); - expect(wrapper.find(pageHelpDropdownSelector)).toHaveLength(1); - expect(wrapper.find(pageUserDropdownSelector)).toHaveLength(1); - }); - - test('dropdowns have expected items and callbacks', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - expect(wrapper.find('DropdownItem')).toHaveLength(0); - wrapper.find(pageHelpDropdownSelector).simulate('click'); - expect(wrapper.find('DropdownItem')).toHaveLength(2); - - const about = wrapper.find('DropdownItem li button'); - about.simulate('click'); - expect(onAboutClick).toHaveBeenCalled(); - - expect(wrapper.find('DropdownItem')).toHaveLength(0); - wrapper.find(pageUserDropdownSelector).simulate('click'); - wrapper.update(); - expect( - wrapper.find('DropdownItem[aria-label="User details"]').prop('href') - ).toBe('#/users/1/details'); - expect(wrapper.find('DropdownItem')).toHaveLength(2); - - const logout = wrapper.find('DropdownItem li button'); - logout.simulate('click'); - expect(onLogoutClick).toHaveBeenCalled(); - }); - - test('pending workflow approvals count set correctly', async () => { - WorkflowApprovalsAPI.read.mockResolvedValueOnce({ - data: { - count: 20, - }, - }); - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - - expect( - wrapper.find('NotificationBadge#toolbar-workflow-approval-badge').text() - ).toEqual('20'); - }); -}); diff --git a/awx/ui/src/components/AppContainer/index.js b/awx/ui/src/components/AppContainer/index.js deleted file mode 100644 index 4fa9f5e563fe..000000000000 --- a/awx/ui/src/components/AppContainer/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import AppContainer from './AppContainer'; - -export default AppContainer; diff --git a/awx/ui/src/components/AppContainer/useWsPendingApprovalCount.js b/awx/ui/src/components/AppContainer/useWsPendingApprovalCount.js deleted file mode 100644 index 0b6866b1ea80..000000000000 --- a/awx/ui/src/components/AppContainer/useWsPendingApprovalCount.js +++ /dev/null @@ -1,39 +0,0 @@ -import { useState, useEffect } from 'react'; -import useWebsocket from 'hooks/useWebsocket'; -import useThrottle from 'hooks/useThrottle'; - -export default function useWsPendingApprovalCount( - initialCount, - fetchApprovalsCount -) { - const [pendingApprovalCount, setPendingApprovalCount] = - useState(initialCount); - const [reloadCount, setReloadCount] = useState(false); - const throttledFetch = useThrottle(reloadCount, 1000); - const lastMessage = useWebsocket({ - jobs: ['status_changed'], - control: ['limit_reached_1'], - }); - - useEffect(() => { - setPendingApprovalCount(initialCount); - }, [initialCount]); - - useEffect(() => { - (async () => { - if (!throttledFetch) { - return; - } - setReloadCount(false); - fetchApprovalsCount(); - })(); - }, [throttledFetch, fetchApprovalsCount]); - - useEffect(() => { - if (lastMessage?.type === 'workflow_approval') { - setReloadCount(true); - } - }, [lastMessage]); - - return pendingApprovalCount; -} diff --git a/awx/ui/src/components/AppContainer/useWsPendingApprovalCount.test.js b/awx/ui/src/components/AppContainer/useWsPendingApprovalCount.test.js deleted file mode 100644 index 3ced90e9df39..000000000000 --- a/awx/ui/src/components/AppContainer/useWsPendingApprovalCount.test.js +++ /dev/null @@ -1,116 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import WS from 'jest-websocket-mock'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import useWsPendingApprovalCount from './useWsPendingApprovalCount'; - -function TestInner() { - return
; -} -function Test({ initialCount, fetchApprovalsCount }) { - const updatedWorkflowApprovals = useWsPendingApprovalCount( - initialCount, - fetchApprovalsCount - ); - return ; -} - -describe('useWsPendingApprovalCount hook', () => { - let debug; - let wrapper; - beforeEach(() => { - /* - Jest mock timers don’t play well with jest-websocket-mock, - so we'll stub out throttling to resolve immediately - */ - jest.mock('../../hooks/useThrottle', () => ({ - __esModule: true, - default: jest.fn((val) => val), - })); - debug = global.console.debug; // eslint-disable-line prefer-destructuring - global.console.debug = () => {}; - }); - - afterEach(() => { - global.console.debug = debug; - WS.clean(); - }); - - test('should return workflow approval pending count', () => { - wrapper = mountWithContexts( - {}} /> - ); - - expect(wrapper.find('TestInner').prop('initialCount')).toEqual(2); - }); - - test('should establish websocket connection', async () => { - global.document.cookie = 'csrftoken=abc123'; - const mockServer = new WS('ws://localhost/websocket/'); - - await act(async () => { - wrapper = mountWithContexts( - {}} /> - ); - }); - - await mockServer.connected; - await expect(mockServer).toReceiveMessage( - JSON.stringify({ - xrftoken: 'abc123', - groups: { - jobs: ['status_changed'], - control: ['limit_reached_1'], - }, - }) - ); - }); - - test('should refetch count after approval status changes', async () => { - global.document.cookie = 'csrftoken=abc123'; - const mockServer = new WS('ws://localhost/websocket/'); - const fetchApprovalsCount = jest.fn(() => []); - await act(async () => { - wrapper = await mountWithContexts( - - ); - }); - - await mockServer.connected; - await act(async () => { - mockServer.send( - JSON.stringify({ - unified_job_id: 2, - type: 'workflow_approval', - status: 'pending', - }) - ); - }); - - expect(fetchApprovalsCount).toHaveBeenCalledTimes(1); - }); - - test('should not refetch when message is not workflow approval', async () => { - global.document.cookie = 'csrftoken=abc123'; - const mockServer = new WS('ws://localhost/websocket/'); - const fetchApprovalsCount = jest.fn(() => []); - await act(async () => { - wrapper = await mountWithContexts( - - ); - }); - - await mockServer.connected; - await act(async () => { - mockServer.send( - JSON.stringify({ - unified_job_id: 1, - type: 'job', - status: 'successful', - }) - ); - }); - - expect(fetchApprovalsCount).toHaveBeenCalledTimes(0); - }); -}); diff --git a/awx/ui/src/components/AppendBody/AppendBody.js b/awx/ui/src/components/AppendBody/AppendBody.js deleted file mode 100644 index 9f3ca64d0608..000000000000 --- a/awx/ui/src/components/AppendBody/AppendBody.js +++ /dev/null @@ -1,17 +0,0 @@ -import { useEffect, useState } from 'react'; -import ReactDOM from 'react-dom'; - -function AppendBody({ children }) { - const [el] = useState(document.createElement('div')); - - useEffect(() => { - document.body.appendChild(el); - return () => { - document.body.removeChild(el); - }; - }, [el]); - - return ReactDOM.createPortal(children, el); -} - -export default AppendBody; diff --git a/awx/ui/src/components/AppendBody/index.js b/awx/ui/src/components/AppendBody/index.js deleted file mode 100644 index d2c2b8ef93fa..000000000000 --- a/awx/ui/src/components/AppendBody/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AppendBody'; diff --git a/awx/ui/src/components/AssociateModal/AssociateModal.js b/awx/ui/src/components/AssociateModal/AssociateModal.js deleted file mode 100644 index 3b42c6691517..000000000000 --- a/awx/ui/src/components/AssociateModal/AssociateModal.js +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { useHistory } from 'react-router-dom'; - -import { t } from '@lingui/macro'; -import { Button, Modal } from '@patternfly/react-core'; -import { getSearchableKeys } from 'components/PaginatedTable'; -import useRequest from 'hooks/useRequest'; -import { getQSConfig, parseQueryString } from 'util/qs'; -import useSelected from 'hooks/useSelected'; -import OptionsList from '../OptionsList'; - -const QS_CONFIG = (order_by = 'name') => - getQSConfig('associate', { - page: 1, - page_size: 5, - order_by, - }); - -function AssociateModal({ - header = t`Items`, - columns = [], - title = t`Select Items`, - onClose, - onAssociate, - fetchRequest, - optionsRequest, - isModalOpen = false, - displayKey = 'name', - ouiaId, -}) { - const history = useHistory(); - const { selected, handleSelect } = useSelected([]); - - const { - request: fetchItems, - result: { items, itemCount, relatedSearchableKeys, searchableKeys }, - error: contentError, - isLoading, - } = useRequest( - useCallback(async () => { - const params = parseQueryString( - QS_CONFIG(displayKey), - history.location.search - ); - const [ - { - data: { count, results }, - }, - actionsResponse, - ] = await Promise.all([fetchRequest(params), optionsRequest()]); - - return { - items: results, - itemCount: count, - relatedSearchableKeys: ( - actionsResponse?.data?.related_search_fields || [] - ).map((val) => val.slice(0, -8)), - searchableKeys: getSearchableKeys(actionsResponse.data.actions?.GET), - }; - }, [fetchRequest, optionsRequest, history.location.search, displayKey]), - { - items: [], - itemCount: 0, - relatedSearchableKeys: [], - searchableKeys: [], - } - ); - - useEffect(() => { - fetchItems(); - }, [fetchItems]); - - const clearQSParams = () => { - const parts = history.location.search.replace(/^\?/, '').split('&'); - const { namespace } = QS_CONFIG(displayKey); - const otherParts = parts.filter( - (param) => !param.startsWith(`${namespace}.`) - ); - history.replace(`${history.location.pathname}?${otherParts.join('&')}`); - }; - - const handleSave = async () => { - await onAssociate(selected); - clearQSParams(); - onClose(); - }; - - const handleClose = () => { - clearQSParams(); - onClose(); - }; - - return ( - - {t`Save`} - , - , - ]} - > - - - ); -} - -export default AssociateModal; diff --git a/awx/ui/src/components/AssociateModal/AssociateModal.test.js b/awx/ui/src/components/AssociateModal/AssociateModal.test.js deleted file mode 100644 index e12e9e748300..000000000000 --- a/awx/ui/src/components/AssociateModal/AssociateModal.test.js +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; - -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import AssociateModal from './AssociateModal'; -import mockHosts from './data.hosts.json'; - -jest.mock('../../api'); - -describe('', () => { - let wrapper; - let onClose; - let onAssociate; - let fetchRequest; - let optionsRequest; - - beforeEach(async () => { - onClose = jest.fn(); - onAssociate = jest.fn().mockResolvedValue(); - fetchRequest = jest.fn().mockReturnValue({ data: { ...mockHosts } }); - optionsRequest = jest.fn().mockResolvedValue({ - data: { - actions: { - GET: {}, - POST: {}, - }, - related_search_fields: [], - }, - }); - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should render successfully', () => { - expect(wrapper.find('AssociateModal').length).toBe(1); - }); - - test('should fetch and render list items', () => { - expect(fetchRequest).toHaveBeenCalledTimes(1); - expect(optionsRequest).toHaveBeenCalledTimes(1); - expect(wrapper.find('CheckboxListItem').length).toBe(3); - }); - - test('should update selected list chips when items are selected', () => { - expect(wrapper.find('SelectedList Chip')).toHaveLength(0); - act(() => { - wrapper.find('CheckboxListItem').first().invoke('onSelect')(); - }); - wrapper.update(); - expect(wrapper.find('SelectedList Chip')).toHaveLength(1); - wrapper.find('SelectedList Chip button').simulate('click'); - expect(wrapper.find('SelectedList Chip')).toHaveLength(0); - }); - - test('save button should call onAssociate', () => { - act(() => { - wrapper.find('CheckboxListItem').first().invoke('onSelect')(); - }); - wrapper.find('button[aria-label="Save"]').simulate('click'); - expect(onAssociate).toHaveBeenCalledTimes(1); - }); - - test('cancel button should call onClose', () => { - wrapper.find('button[aria-label="Cancel"]').simulate('click'); - expect(onClose).toHaveBeenCalledTimes(1); - }); -}); diff --git a/awx/ui/src/components/AssociateModal/data.hosts.json b/awx/ui/src/components/AssociateModal/data.hosts.json deleted file mode 100644 index 07c6ef7d9f8d..000000000000 --- a/awx/ui/src/components/AssociateModal/data.hosts.json +++ /dev/null @@ -1,393 +0,0 @@ - -{ - "count": 3, - "results": [ - { - "id": 2, - "type": "host", - "url": "/api/v2/hosts/2/", - "related": { - "created_by": "/api/v2/users/10/", - "modified_by": "/api/v2/users/19/", - "variable_data": "/api/v2/hosts/2/variable_data/", - "groups": "/api/v2/hosts/2/groups/", - "all_groups": "/api/v2/hosts/2/all_groups/", - "job_events": "/api/v2/hosts/2/job_events/", - "job_host_summaries": "/api/v2/hosts/2/job_host_summaries/", - "activity_stream": "/api/v2/hosts/2/activity_stream/", - "inventory_sources": "/api/v2/hosts/2/inventory_sources/", - "smart_inventories": "/api/v2/hosts/2/smart_inventories/", - "ad_hoc_commands": "/api/v2/hosts/2/ad_hoc_commands/", - "ad_hoc_command_events": "/api/v2/hosts/2/ad_hoc_command_events/", - "insights": "/api/v2/hosts/2/insights/", - "ansible_facts": "/api/v2/hosts/2/ansible_facts/", - "inventory": "/api/v2/inventories/2/", - "last_job": "/api/v2/jobs/236/", - "last_job_host_summary": "/api/v2/job_host_summaries/2202/" - }, - "summary_fields": { - "inventory": { - "id": 2, - "name": " Inventory 1 Org 0", - "description": "", - "has_active_failures": false, - "total_hosts": 33, - "hosts_with_active_failures": 0, - "total_groups": 4, - "has_inventory_sources": false, - "total_inventory_sources": 0, - "inventory_sources_with_failures": 0, - "organization_id": 2, - "kind": "" - }, - "last_job": { - "id": 236, - "name": " Job Template 1 Project 0", - "description": "", - "finished": "2020-02-26T03:15:21.471439Z", - "status": "successful", - "failed": false, - "job_template_id": 18, - "job_template_name": " Job Template 1 Project 0" - }, - "last_job_host_summary": { - "id": 2202, - "failed": false - }, - "created_by": { - "id": 10, - "username": "user-3", - "first_name": "", - "last_name": "" - }, - "modified_by": { - "id": 19, - "username": "all", - "first_name": "", - "last_name": "" - }, - "user_capabilities": { - "edit": true, - "delete": true - }, - "groups": { - "count": 2, - "results": [ - { - "id": 1, - "name": " Group 1 Inventory 0" - }, - { - "id": 2, - "name": " Group 2 Inventory 0" - } - ] - }, - "recent_jobs": [ - { - "id": 236, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-26T03:15:21.471439Z" - }, - { - "id": 232, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T21:20:33.593789Z" - }, - { - "id": 229, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T16:19:46.364134Z" - }, - { - "id": 228, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T16:18:54.138363Z" - }, - { - "id": 225, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T15:55:32.247652Z" - } - ] - }, - "created": "2020-02-24T15:10:58.922179Z", - "modified": "2020-02-26T21:52:43.428530Z", - "name": ".host-000001.group-00000.dummy", - "description": "", - "inventory": 2, - "enabled": false, - "instance_id": "", - "variables": "", - "has_active_failures": false, - "has_inventory_sources": false, - "last_job": 236, - "last_job_host_summary": 2202, - "insights_system_id": null, - "ansible_facts_modified": null - }, - { - "id": 3, - "type": "host", - "url": "/api/v2/hosts/3/", - "related": { - "created_by": "/api/v2/users/11/", - "modified_by": "/api/v2/users/1/", - "variable_data": "/api/v2/hosts/3/variable_data/", - "groups": "/api/v2/hosts/3/groups/", - "all_groups": "/api/v2/hosts/3/all_groups/", - "job_events": "/api/v2/hosts/3/job_events/", - "job_host_summaries": "/api/v2/hosts/3/job_host_summaries/", - "activity_stream": "/api/v2/hosts/3/activity_stream/", - "inventory_sources": "/api/v2/hosts/3/inventory_sources/", - "smart_inventories": "/api/v2/hosts/3/smart_inventories/", - "ad_hoc_commands": "/api/v2/hosts/3/ad_hoc_commands/", - "ad_hoc_command_events": "/api/v2/hosts/3/ad_hoc_command_events/", - "insights": "/api/v2/hosts/3/insights/", - "ansible_facts": "/api/v2/hosts/3/ansible_facts/", - "inventory": "/api/v2/inventories/2/", - "last_job": "/api/v2/jobs/236/", - "last_job_host_summary": "/api/v2/job_host_summaries/2195/" - }, - "summary_fields": { - "inventory": { - "id": 2, - "name": " Inventory 1 Org 0", - "description": "", - "has_active_failures": false, - "total_hosts": 33, - "hosts_with_active_failures": 0, - "total_groups": 4, - "has_inventory_sources": false, - "total_inventory_sources": 0, - "inventory_sources_with_failures": 0, - "organization_id": 2, - "kind": "" - }, - "last_job": { - "id": 236, - "name": " Job Template 1 Project 0", - "description": "", - "finished": "2020-02-26T03:15:21.471439Z", - "status": "successful", - "failed": false, - "job_template_id": 18, - "job_template_name": " Job Template 1 Project 0" - }, - "last_job_host_summary": { - "id": 2195, - "failed": false - }, - "created_by": { - "id": 11, - "username": "user-4", - "first_name": "", - "last_name": "" - }, - "modified_by": { - "id": 1, - "username": "admin", - "first_name": "", - "last_name": "" - }, - "user_capabilities": { - "edit": true, - "delete": true - }, - "groups": { - "count": 2, - "results": [ - { - "id": 1, - "name": " Group 1 Inventory 0" - }, - { - "id": 2, - "name": " Group 2 Inventory 0" - } - ] - }, - "recent_jobs": [ - { - "id": 236, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-26T03:15:21.471439Z" - }, - { - "id": 232, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T21:20:33.593789Z" - }, - { - "id": 229, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T16:19:46.364134Z" - }, - { - "id": 228, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T16:18:54.138363Z" - }, - { - "id": 225, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T15:55:32.247652Z" - } - ] - }, - "created": "2020-02-24T15:10:58.945113Z", - "modified": "2020-02-27T03:43:43.635871Z", - "name": ".host-000002.group-00000.dummy", - "description": "", - "inventory": 2, - "enabled": false, - "instance_id": "", - "variables": "", - "has_active_failures": false, - "has_inventory_sources": false, - "last_job": 236, - "last_job_host_summary": 2195, - "insights_system_id": null, - "ansible_facts_modified": null - }, - { - "id": 4, - "type": "host", - "url": "/api/v2/hosts/4/", - "related": { - "created_by": "/api/v2/users/12/", - "modified_by": "/api/v2/users/1/", - "variable_data": "/api/v2/hosts/4/variable_data/", - "groups": "/api/v2/hosts/4/groups/", - "all_groups": "/api/v2/hosts/4/all_groups/", - "job_events": "/api/v2/hosts/4/job_events/", - "job_host_summaries": "/api/v2/hosts/4/job_host_summaries/", - "activity_stream": "/api/v2/hosts/4/activity_stream/", - "inventory_sources": "/api/v2/hosts/4/inventory_sources/", - "smart_inventories": "/api/v2/hosts/4/smart_inventories/", - "ad_hoc_commands": "/api/v2/hosts/4/ad_hoc_commands/", - "ad_hoc_command_events": "/api/v2/hosts/4/ad_hoc_command_events/", - "insights": "/api/v2/hosts/4/insights/", - "ansible_facts": "/api/v2/hosts/4/ansible_facts/", - "inventory": "/api/v2/inventories/2/", - "last_job": "/api/v2/jobs/236/", - "last_job_host_summary": "/api/v2/job_host_summaries/2192/" - }, - "summary_fields": { - "inventory": { - "id": 2, - "name": " Inventory 1 Org 0", - "description": "", - "has_active_failures": false, - "total_hosts": 33, - "hosts_with_active_failures": 0, - "total_groups": 4, - "has_inventory_sources": false, - "total_inventory_sources": 0, - "inventory_sources_with_failures": 0, - "organization_id": 2, - "kind": "" - }, - "last_job": { - "id": 236, - "name": " Job Template 1 Project 0", - "description": "", - "finished": "2020-02-26T03:15:21.471439Z", - "status": "successful", - "failed": false, - "job_template_id": 18, - "job_template_name": " Job Template 1 Project 0" - }, - "last_job_host_summary": { - "id": 2192, - "failed": false - }, - "created_by": { - "id": 12, - "username": "user-5", - "first_name": "", - "last_name": "" - }, - "modified_by": { - "id": 1, - "username": "admin", - "first_name": "", - "last_name": "" - }, - "user_capabilities": { - "edit": true, - "delete": true - }, - "groups": { - "count": 2, - "results": [ - { - "id": 1, - "name": " Group 1 Inventory 0" - }, - { - "id": 2, - "name": " Group 2 Inventory 0" - } - ] - }, - "recent_jobs": [ - { - "id": 236, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-26T03:15:21.471439Z" - }, - { - "id": 232, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T21:20:33.593789Z" - }, - { - "id": 229, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T16:19:46.364134Z" - }, - { - "id": 228, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T16:18:54.138363Z" - }, - { - "id": 225, - "name": " Job Template 1 Project 0", - "status": "successful", - "finished": "2020-02-25T15:55:32.247652Z" - } - ] - }, - "created": "2020-02-24T15:10:58.962312Z", - "modified": "2020-02-27T03:43:45.528882Z", - "name": ".host-000003.group-00000.dummy", - "description": "", - "inventory": 2, - "enabled": false, - "instance_id": "", - "variables": "", - "has_active_failures": false, - "has_inventory_sources": false, - "last_job": 236, - "last_job_host_summary": 2192, - "insights_system_id": null, - "ansible_facts_modified": null - } - ] -} diff --git a/awx/ui/src/components/AssociateModal/index.js b/awx/ui/src/components/AssociateModal/index.js deleted file mode 100644 index 1a9df3aa33c8..000000000000 --- a/awx/ui/src/components/AssociateModal/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AssociateModal'; diff --git a/awx/ui/src/components/Background/Background.js b/awx/ui/src/components/Background/Background.js deleted file mode 100644 index c6521168b5b4..000000000000 --- a/awx/ui/src/components/Background/Background.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -import { BackgroundImage } from '@patternfly/react-core'; - -export default ({ children }) => ( - <> - - {children} - -); diff --git a/awx/ui/src/components/Background/Background.test.js b/awx/ui/src/components/Background/Background.test.js deleted file mode 100644 index 4d306f79a5a1..000000000000 --- a/awx/ui/src/components/Background/Background.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; - -import Background from './Background'; - -describe('Background', () => { - test('renders the expected content', () => { - const wrapper = mount( - -
- - ); - expect(wrapper).toHaveLength(1); - expect(wrapper.find('.pf-c-background-image')).toHaveLength(1); - expect(wrapper.find('#test')).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/Background/index.js b/awx/ui/src/components/Background/index.js deleted file mode 100644 index 5f61d4bc31ce..000000000000 --- a/awx/ui/src/components/Background/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Background'; diff --git a/awx/ui/src/components/Card/CardActionsRow.js b/awx/ui/src/components/Card/CardActionsRow.js deleted file mode 100644 index 3652692a8150..000000000000 --- a/awx/ui/src/components/Card/CardActionsRow.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import { CardActions } from '@patternfly/react-core'; -import styled from 'styled-components'; - -const CardActionsWrapper = styled.div` - margin-top: 20px; - --pf-c-card__actions--PaddingLeft: 0; -`; - -function CardActionsRow({ children }) { - return ( - - {children} - - ); -} - -export default CardActionsRow; diff --git a/awx/ui/src/components/Card/CardBody.js b/awx/ui/src/components/Card/CardBody.js deleted file mode 100644 index 095eece1429c..000000000000 --- a/awx/ui/src/components/Card/CardBody.js +++ /dev/null @@ -1,9 +0,0 @@ -import styled from 'styled-components'; -import { CardBody } from '@patternfly/react-core'; - -const TabbedCardBody = styled(CardBody)` - padding-top: var(--pf-c-card--first-child--PaddingTop); -`; -CardBody.displayName = 'PFCardBody'; - -export default TabbedCardBody; diff --git a/awx/ui/src/components/Card/index.js b/awx/ui/src/components/Card/index.js deleted file mode 100644 index 93de96efcabf..000000000000 --- a/awx/ui/src/components/Card/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as CardBody } from './CardBody'; -export { default as CardActionsRow } from './CardActionsRow'; diff --git a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js b/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js deleted file mode 100644 index bce6a322c2e4..000000000000 --- a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.js +++ /dev/null @@ -1,90 +0,0 @@ -import 'styled-components/macro'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { t } from '@lingui/macro'; -import { Td, Tr } from '@patternfly/react-table'; -import { ActionsTd } from 'components/PaginatedTable'; - -const CheckboxListItem = ({ - isRadio = false, - isSelected = false, - itemId, - label, - name, - onDeselect, - rowIndex, - onSelect, - columns, - item, - rowActions, -}) => { - const handleRowClick = () => { - if (isSelected && !isRadio) { - onDeselect(itemId); - } else { - onSelect(itemId); - } - }; - - return ( - - - - {columns?.length > 0 ? ( - columns.map((col) => ( - - {item[col.key]} - - )) - ) : ( - - {label} - - )} - {rowActions && ( - - {rowActions.map((rowAction) => { - const { - props: { id }, - } = rowAction; - return {rowAction}; - })} - - )} - - ); -}; - -CheckboxListItem.propTypes = { - isSelected: PropTypes.bool.isRequired, - itemId: PropTypes.number.isRequired, - label: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - onDeselect: PropTypes.func.isRequired, - onSelect: PropTypes.func.isRequired, -}; - -export default CheckboxListItem; diff --git a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js b/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js deleted file mode 100644 index fe54582de28c..000000000000 --- a/awx/ui/src/components/CheckboxListItem/CheckboxListItem.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; - -import CheckboxListItem from './CheckboxListItem'; - -describe('CheckboxListItem', () => { - test('renders the expected content', () => { - const wrapper = mount( - - - {}} - onDeselect={() => {}} - /> - -
- ); - expect(wrapper).toHaveLength(1); - }); - - test('should render row actions', () => { - const wrapper = mount( - - - {}} - onDeselect={() => {}} - rowActions={[ -
action_1
, -
action_2
, - ]} - /> -
-
- ); - expect( - wrapper - .find('ActionsTd') - .containsAllMatchingElements([ -
action_1
, -
action_2
, - ]) - ).toEqual(true); - }); -}); diff --git a/awx/ui/src/components/CheckboxListItem/index.js b/awx/ui/src/components/CheckboxListItem/index.js deleted file mode 100644 index f1c4287a66b5..000000000000 --- a/awx/ui/src/components/CheckboxListItem/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CheckboxListItem'; diff --git a/awx/ui/src/components/ChipGroup/ChipGroup.js b/awx/ui/src/components/ChipGroup/ChipGroup.js deleted file mode 100644 index a4ad262b89ad..000000000000 --- a/awx/ui/src/components/ChipGroup/ChipGroup.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -import { t } from '@lingui/macro'; -import { ChipGroup as PFChipGroup } from '@patternfly/react-core'; -import { number } from 'prop-types'; - -function ChipGroup({ numChips, totalChips, ...props }) { - return ( - - ); -} - -ChipGroup.propTypes = { - numChips: number.isRequired, - totalChips: number.isRequired, -}; - -export default ChipGroup; diff --git a/awx/ui/src/components/ChipGroup/ChipGroup.test.js b/awx/ui/src/components/ChipGroup/ChipGroup.test.js deleted file mode 100644 index e9d843e77133..000000000000 --- a/awx/ui/src/components/ChipGroup/ChipGroup.test.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import ChipGroup from './ChipGroup'; - -describe('ChipGroup', () => { - test('should mount properly', () => { - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('ChipGroup').at(1).props().collapsedText).toEqual( - '5 more' - ); - }); -}); diff --git a/awx/ui/src/components/ChipGroup/index.js b/awx/ui/src/components/ChipGroup/index.js deleted file mode 100644 index e38952cc63bd..000000000000 --- a/awx/ui/src/components/ChipGroup/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ChipGroup'; diff --git a/awx/ui/src/components/CodeEditor/CodeEditor.js b/awx/ui/src/components/CodeEditor/CodeEditor.js deleted file mode 100644 index d7cc1f5e9f65..000000000000 --- a/awx/ui/src/components/CodeEditor/CodeEditor.js +++ /dev/null @@ -1,206 +0,0 @@ -import React, { useEffect, useRef, useCallback } from 'react'; -import { oneOf, bool, number, string, func, oneOfType } from 'prop-types'; - -import ReactAce from 'react-ace'; -import 'ace-builds/src-noconflict/mode-json'; -import 'ace-builds/src-noconflict/mode-javascript'; -import 'ace-builds/src-noconflict/mode-yaml'; -import 'ace-builds/src-noconflict/mode-django'; -import 'ace-builds/src-noconflict/theme-github'; - -import { t } from '@lingui/macro'; -import styled from 'styled-components'; -import debounce from 'util/debounce'; - -const LINE_HEIGHT = 24; -const PADDING = 12; - -const FocusWrapper = styled.div` - && + .keyboard-help-text { - opacity: 0; - transition: opacity 0.1s linear; - } - - &:focus-within + .keyboard-help-text { - opacity: 1; - } - - & .ace_hidden-cursors .ace_cursor { - opacity: 0; - } -`; - -const AceEditor = styled(ReactAce)` - font-family: var(--pf-global--FontFamily--monospace); - max-height: 90vh; - - & .ace_gutter, - & .ace_scroller { - padding-top: 4px; - padding-bottom: 4px; - } - - & .ace_mobile-menu { - display: none; - } - - ${(props) => - props.hasErrors && - ` - && { - --pf-c-form-control--PaddingRight: var(--pf-c-form-control--invalid--PaddingRight); - --pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--invalid--BorderBottomColor); - padding-right: 24px; - padding-bottom: var(--pf-c-form-control--invalid--PaddingBottom); - background: var(--pf-c-form-control--invalid--Background); - border-bottom-width: var(--pf-c-form-control--invalid--BorderBottomWidth); - }`} - - ${(props) => - props.setOptions.readOnly && - ` - && .ace_cursor { - opacity: 0; - } - `} -`; -AceEditor.displayName = 'AceEditor'; - -function CodeEditor({ - id, - value, - onChange, - onFocus, - onBlur, - mode, - readOnly, - hasErrors, - rows, - fullHeight, - className, -}) { - if (rows && typeof rows !== 'number' && rows !== 'auto') { - // eslint-disable-next-line no-console - console.warn( - `CodeEditor: Unexpected value for 'rows': ${rows}; expected number or 'auto'` - ); - } - - const wrapper = useRef(null); - const editor = useRef(null); - - useEffect(() => { - const editorInput = editor.current.refEditor?.querySelector('textarea'); - if (!editorInput) { - return; - } - if (!readOnly) { - editorInput.tabIndex = -1; - } - editorInput.id = id; - }, [readOnly, id]); - - const listen = useCallback((event) => { - if (wrapper.current === document.activeElement && event.key === 'Enter') { - const editorInput = editor.current.refEditor?.querySelector('textarea'); - if (!editorInput) { - return; - } - event.preventDefault(); - event.stopPropagation(); - editorInput.focus(); - } - }, []); - - useEffect(() => { - const wrapperEl = wrapper.current; - wrapperEl.addEventListener('keydown', listen); - - return () => { - wrapperEl.removeEventListener('keydown', listen); - }; - }); - - const aceModes = { - javascript: 'json', - yaml: 'yaml', - jinja2: 'django', - }; - - const numRows = rows === 'auto' ? value.split('\n').length : rows; - const height = fullHeight ? '50vh' : `${numRows * LINE_HEIGHT + PADDING}px`; - - return ( - <> - - { - wrapper.current.focus(); - }, - }, - { - name: 'tab escape', - bindKey: { win: 'Shift-Tab', mac: 'Shift-Tab' }, - exec: () => { - wrapper.current.focus(); - }, - }, - ]} - ref={editor} - /> - - {!readOnly && ( -
- {t`Press Enter to edit. Press ESC to stop editing.`} -
- )} - - ); -} -CodeEditor.propTypes = { - value: string.isRequired, - onChange: func, - mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired, - readOnly: bool, - hasErrors: bool, - fullHeight: bool, - rows: oneOfType([number, string]), - className: string, -}; -CodeEditor.defaultProps = { - readOnly: false, - onChange: () => {}, - rows: 6, - fullHeight: false, - hasErrors: false, - className: '', -}; - -export default CodeEditor; diff --git a/awx/ui/src/components/CodeEditor/CodeEditor.test.js b/awx/ui/src/components/CodeEditor/CodeEditor.test.js deleted file mode 100644 index 876106a090d6..000000000000 --- a/awx/ui/src/components/CodeEditor/CodeEditor.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import debounce from 'util/debounce'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import CodeEditor from './CodeEditor'; - -jest.mock('../../util/debounce'); - -describe('CodeEditor', () => { - beforeEach(() => { - document.body.createTextRange = jest.fn(); - }); - - it('should pass value and mode through to ace editor', () => { - const onChange = jest.fn(); - const wrapper = mountWithContexts( - - ); - const aceEditor = wrapper.find('AceEditor'); - expect(aceEditor.prop('mode')).toEqual('yaml'); - expect(aceEditor.prop('setOptions').readOnly).toEqual(false); - expect(aceEditor.prop('value')).toEqual('---\nfoo: bar'); - }); - - it('should trigger onChange prop', () => { - debounce.mockImplementation((fn) => fn); - const onChange = jest.fn(); - const wrapper = mountWithContexts( - - ); - const aceEditor = wrapper.find('AceEditor'); - aceEditor.prop('onChange')('newvalue'); - expect(onChange).toHaveBeenCalledWith('newvalue'); - }); - - it('should render in read only mode', () => { - const onChange = jest.fn(); - const wrapper = mountWithContexts( - - ); - const aceEditor = wrapper.find('AceEditor'); - expect(aceEditor.prop('setOptions').readOnly).toEqual(true); - expect(aceEditor.prop('value')).toEqual('---'); - }); -}); diff --git a/awx/ui/src/components/CodeEditor/CodeEditorField.js b/awx/ui/src/components/CodeEditor/CodeEditorField.js deleted file mode 100644 index ee7f4d9cd4a0..000000000000 --- a/awx/ui/src/components/CodeEditor/CodeEditorField.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import { - string, - oneOfType, - object, - func, - bool, - node, - oneOf, - number, -} from 'prop-types'; -import { useField } from 'formik'; -import { FormGroup } from '@patternfly/react-core'; -import CodeEditor from './CodeEditor'; -import Popover from '../Popover'; - -function CodeEditorField({ - id, - name, - label, - tooltip, - helperText, - validate, - isRequired, - mode, - ...rest -}) { - const [field, meta, helpers] = useField({ name, validate }); - const isValid = !(meta.touched && meta.error); - - return ( - } - > - { - helpers.setValue(value); - }} - mode={mode} - /> - - ); -} -CodeEditorField.propTypes = { - helperText: string, - id: string.isRequired, - name: string.isRequired, - label: oneOfType([object, string]).isRequired, - validate: func, - isRequired: bool, - tooltip: node, - mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired, - rows: number, -}; - -CodeEditorField.defaultProps = { - helperText: '', - validate: () => {}, - isRequired: false, - tooltip: null, - rows: 5, -}; - -export default CodeEditorField; diff --git a/awx/ui/src/components/CodeEditor/VariablesDetail.js b/awx/ui/src/components/CodeEditor/VariablesDetail.js deleted file mode 100644 index c1f69fbf82d0..000000000000 --- a/awx/ui/src/components/CodeEditor/VariablesDetail.js +++ /dev/null @@ -1,224 +0,0 @@ -import 'styled-components/macro'; -import React, { useState } from 'react'; -import { node, number, oneOfType, shape, string, arrayOf } from 'prop-types'; - -import { t } from '@lingui/macro'; -import { - Split, - SplitItem, - TextListItemVariants, - Button, - Modal, -} from '@patternfly/react-core'; -import { ExpandArrowsAltIcon } from '@patternfly/react-icons'; -import { yamlToJson, jsonToYaml, isJsonObject, isJsonString } from 'util/yaml'; -import { DetailName, DetailValue } from '../DetailList'; -import MultiButtonToggle from '../MultiButtonToggle'; -import Popover from '../Popover'; -import CodeEditor from './CodeEditor'; -import { JSON_MODE, YAML_MODE } from './constants'; - -function VariablesDetail({ - dataCy, - helpText, - value, - label, - rows, - fullHeight, - name, -}) { - const [mode, setMode] = useState( - isJsonObject(value) || isJsonString(value) ? JSON_MODE : YAML_MODE - ); - const [isExpanded, setIsExpanded] = useState(false); - - let currentValue = value; - let error; - - const getValueInCurrentMode = () => { - if (!value) { - if (mode === JSON_MODE) { - return '{}'; - } - return '---'; - } - const modeMatches = isJsonString(value) === (mode === JSON_MODE); - if (modeMatches) { - if (mode === JSON_MODE) { - return JSON.stringify(JSON.parse(value), null, 2); - } - return value; - } - return mode === YAML_MODE ? jsonToYaml(value) : yamlToJson(value); - }; - - try { - currentValue = getValueInCurrentMode(); - } catch (err) { - error = err; - } - - const labelCy = dataCy ? `${dataCy}-label` : null; - const valueCy = dataCy ? `${dataCy}-value` : null; - - return ( - <> - - setIsExpanded(true)} - name={name} - /> - - - - {error && ( -
- {t`Error:`} {error.message} -
- )} -
- setIsExpanded(false)} - actions={[ - , - ]} - > -
- - -
-
- - ); -} -VariablesDetail.propTypes = { - value: oneOfType([shape({}), arrayOf(string), string]).isRequired, - label: node.isRequired, - rows: oneOfType([number, string]), - dataCy: string, - helpText: oneOfType([node, string]), - name: string.isRequired, -}; -VariablesDetail.defaultProps = { - rows: null, - dataCy: '', - helpText: '', -}; - -function ModeToggle({ - id, - label, - helpText, - dataCy, - mode, - setMode, - onExpand, - name, -}) { - return ( - - - - - - - - { - setMode(newMode); - }} - name={name} - /> - - - - {onExpand && ( - - - - )} - - ); -} - -export default VariablesDetail; diff --git a/awx/ui/src/components/CodeEditor/VariablesDetail.test.js b/awx/ui/src/components/CodeEditor/VariablesDetail.test.js deleted file mode 100644 index 0ad9fed51bde..000000000000 --- a/awx/ui/src/components/CodeEditor/VariablesDetail.test.js +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import VariablesDetail from './VariablesDetail'; - -jest.mock('../../api'); - -describe('', () => { - test('should render readonly CodeEditor', () => { - const wrapper = mountWithContexts( - - ); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input).toHaveLength(1); - expect(input.prop('mode')).toEqual('yaml'); - expect(input.prop('value')).toEqual('---foo: bar'); - expect(input.prop('readOnly')).toEqual(true); - }); - - test('should detect JSON', () => { - const wrapper = mountWithContexts( - - ); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input).toHaveLength(1); - expect(input.prop('mode')).toEqual('javascript'); - }); - - test('should format JSON', () => { - const wrapper = mountWithContexts( - - ); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input).toHaveLength(1); - expect(input.prop('value')).toEqual('{\n "foo": "bar"\n}'); - }); - - test('should convert between modes', () => { - const wrapper = mountWithContexts( - - ); - wrapper.find('MultiButtonToggle').invoke('onChange')('javascript'); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input.prop('mode')).toEqual('javascript'); - expect(input.prop('value')).toEqual('{\n "foo": "bar"\n}'); - - wrapper.find('MultiButtonToggle').invoke('onChange')('yaml'); - const input2 = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input2.prop('mode')).toEqual('yaml'); - expect(input2.prop('value')).toEqual('---foo: bar'); - }); - - test('should render label and value --- when there are no values', () => { - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('VariablesDetail___StyledCodeEditor').length).toBe(1); - expect(wrapper.find('.pf-c-form__label').text()).toBe('Variables'); - }); - - test('should update value if prop changes', () => { - const wrapper = mountWithContexts( - - ); - act(() => { - wrapper.find('MultiButtonToggle').invoke('onChange')('javascript'); - }); - wrapper.setProps({ - value: '---bar: baz', - }); - wrapper.update(); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input.prop('mode')).toEqual('javascript'); - expect(input.prop('value')).toEqual('{\n "bar": "baz"\n}'); - }); - - test('should default yaml value to "---"', () => { - const wrapper = mountWithContexts( - - ); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input.prop('value')).toEqual('---'); - }); - - test('should default empty json to "{}"', () => { - const wrapper = mountWithContexts( - - ); - act(() => { - wrapper.find('MultiButtonToggle').invoke('onChange')('javascript'); - }); - wrapper.setProps({ value: '' }); - const input = wrapper.find('VariablesDetail___StyledCodeEditor'); - expect(input.prop('value')).toEqual('{}'); - }); -}); diff --git a/awx/ui/src/components/CodeEditor/VariablesField.js b/awx/ui/src/components/CodeEditor/VariablesField.js deleted file mode 100644 index 02ede84f7d64..000000000000 --- a/awx/ui/src/components/CodeEditor/VariablesField.js +++ /dev/null @@ -1,271 +0,0 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { string, bool, func, oneOf } from 'prop-types'; - -import { t } from '@lingui/macro'; -import { useField } from 'formik'; -import styled from 'styled-components'; -import { Split, SplitItem, Button, Modal } from '@patternfly/react-core'; -import { ExpandArrowsAltIcon } from '@patternfly/react-icons'; -import { yamlToJson, jsonToYaml, isJsonString } from 'util/yaml'; -import { CheckboxField } from '../FormField'; -import MultiButtonToggle from '../MultiButtonToggle'; -import CodeEditor from './CodeEditor'; -import Popover from '../Popover'; -import { JSON_MODE, YAML_MODE } from './constants'; - -const FieldHeader = styled.div` - display: flex; - justify-content: space-between; - padding-bottom: var(--pf-c-form__group-label--PaddingBottom); -`; - -const StyledCheckboxField = styled(CheckboxField)` - --pf-c-check__label--FontSize: var(--pf-c-form__label--FontSize); - margin-left: auto; -`; - -function VariablesField({ - id, - name, - label, - readOnly, - promptId, - tooltip, - initialMode, - onModeChange, -}) { - // track focus manually, because the Code Editor library doesn't wire - // into Formik completely - const [shouldValidate, setShouldValidate] = useState(false); - const [mode, setMode] = useState(initialMode || YAML_MODE); - const validate = useCallback( - (value) => { - if (!shouldValidate) { - return undefined; - } - try { - if (mode === YAML_MODE) { - yamlToJson(value); - } else { - JSON.parse(value); - } - } catch (error) { - return error.message; - } - return undefined; - }, - [shouldValidate, mode] - ); - const [field, meta, helpers] = useField({ name, validate }); - - useEffect(() => { - if (isJsonString(field.value)) { - // mode's useState above couldn't be initialized to JSON_MODE because - // the field value had to be defined below it - setMode(JSON_MODE); - onModeChange(JSON_MODE); - helpers.setValue(JSON.stringify(JSON.parse(field.value), null, 2)); - } - }, []); // eslint-disable-line react-hooks/exhaustive-deps - - useEffect( - () => { - if (shouldValidate) { - helpers.setError(validate(field.value)); - } - }, - [shouldValidate, validate] // eslint-disable-line react-hooks/exhaustive-deps - ); - const [lastYamlValue, setLastYamlValue] = useState( - mode === YAML_MODE ? field.value : null - ); - const [isJsonEdited, setIsJsonEdited] = useState(false); - const [isExpanded, setIsExpanded] = useState(false); - - const handleModeChange = (newMode) => { - if (newMode === YAML_MODE && !isJsonEdited && lastYamlValue !== null) { - helpers.setValue(lastYamlValue, false); - setMode(newMode); - onModeChange(newMode); - return; - } - - try { - const newVal = - newMode === YAML_MODE - ? jsonToYaml(field.value) - : yamlToJson(field.value); - helpers.setValue(newVal, false); - setMode(newMode); - onModeChange(newMode); - } catch (err) { - helpers.setError(err.message); - } - }; - - const handleChange = (newVal) => { - helpers.setValue(newVal); - if (mode === JSON_MODE) { - setIsJsonEdited(true); - } else { - setLastYamlValue(newVal); - setIsJsonEdited(false); - } - }; - - return ( -
- setIsExpanded(true)} - mode={mode} - setMode={handleModeChange} - setShouldValidate={setShouldValidate} - handleChange={handleChange} - /> - setIsExpanded(false)} - actions={[ - , - ]} - > -
- -
-
- {meta.error ? ( -
- {meta.error} -
- ) : null} -
- ); -} -VariablesField.propTypes = { - id: string.isRequired, - name: string.isRequired, - label: string.isRequired, - readOnly: bool, - promptId: string, - initialMode: oneOf([YAML_MODE, JSON_MODE]), - onModeChange: func, -}; -VariablesField.defaultProps = { - readOnly: false, - promptId: null, - initialMode: YAML_MODE, - onModeChange: () => {}, -}; - -function VariablesFieldInternals({ - id, - name, - label, - readOnly, - promptId, - tooltip, - fullHeight, - mode, - setMode, - onExpand, - setShouldValidate, - handleChange, -}) { - const [field, meta, helpers] = useField(name); - - useEffect(() => { - if (mode === YAML_MODE) { - return; - } - try { - helpers.setValue(JSON.stringify(JSON.parse(field.value), null, 2)); - } catch (e) { - helpers.setError(e.message); - } - }, []); // eslint-disable-line react-hooks/exhaustive-deps - - return ( -
- - - - - {tooltip && } - - - - - - {promptId && ( - - )} - {onExpand && ( - - )} - - setShouldValidate(false)} - onBlur={() => setShouldValidate(true)} - hasErrors={!!meta.error} - /> -
- ); -} - -export default VariablesField; diff --git a/awx/ui/src/components/CodeEditor/VariablesField.test.js b/awx/ui/src/components/CodeEditor/VariablesField.test.js deleted file mode 100644 index 551de4fe3fed..000000000000 --- a/awx/ui/src/components/CodeEditor/VariablesField.test.js +++ /dev/null @@ -1,270 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { Formik } from 'formik'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import VariablesField from './VariablesField'; - -describe('VariablesField', () => { - it('should render code editor', () => { - const value = '---\n'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - const codeEditor = wrapper.find('CodeEditor'); - expect(codeEditor.prop('value')).toEqual(value); - }); - - it('should toggle between yaml/json', async () => { - const value = '---\nfoo: bar\nbaz: 3'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - const buttons = wrapper.find('Button'); - expect(buttons).toHaveLength(3); - expect(buttons.at(0).prop('variant')).toEqual('primary'); - expect(buttons.at(1).prop('variant')).toEqual('secondary'); - await act(async () => { - buttons.at(1).simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('CodeEditor').prop('mode')).toEqual('javascript'); - expect(wrapper.find('CodeEditor').prop('value')).toEqual( - '{\n "foo": "bar",\n "baz": 3\n}' - ); - const buttons2 = wrapper.find('Button'); - expect(buttons2.at(0).prop('variant')).toEqual('secondary'); - expect(buttons2.at(1).prop('variant')).toEqual('primary'); - await act(async () => { - buttons2.at(0).simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('CodeEditor').prop('mode')).toEqual('yaml'); - expect(wrapper.find('CodeEditor').prop('value')).toEqual( - '---\nfoo: bar\nbaz: 3' - ); - }); - - it('should retain non-expanded yaml if JSON value not edited', async () => { - const value = '---\na: &aa [a,b,c]\nb: *aa'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - const jsButton = wrapper.find('Button.toggle-button-javascript'); - await act(async () => { - jsButton.simulate('click'); - }); - wrapper.update(); - const yamlButton = wrapper.find('Button.toggle-button-yaml'); - await act(async () => { - yamlButton.simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('CodeEditor').prop('mode')).toEqual('yaml'); - expect(wrapper.find('CodeEditor').prop('value')).toEqual(value); - }); - - it('should retain expanded yaml if JSON value is edited', async () => { - const value = '---\na: &aa [a,b,c]\nb: *aa'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - const jsButton = wrapper.find('Button.toggle-button-javascript'); - await act(async () => { - jsButton.simulate('click'); - }); - wrapper.update(); - wrapper.find('CodeEditor').invoke('onChange')( - '{\n "foo": "bar",\n "baz": 3\n}' - ); - const yamlButton = wrapper.find('Button.toggle-button-yaml'); - await act(async () => { - yamlButton.simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('CodeEditor').prop('mode')).toEqual('yaml'); - expect(wrapper.find('CodeEditor').prop('value')).toEqual( - 'foo: bar\nbaz: 3\n' - ); - }); - - it('should retain non-expanded yaml if YAML value is edited', async () => { - const value = '---\na: &aa [a,b,c]\nb: *aa'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - wrapper.find('CodeEditor').invoke('onChange')( - '---\na: &aa [a,b,c]\nb: *aa\n' - ); - const buttons = wrapper.find('Button'); - await act(async () => { - buttons.at(1).simulate('click'); - }); - wrapper.update(); - const buttons2 = wrapper.find('Button'); - await act(async () => { - buttons2.at(0).simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('CodeEditor').prop('mode')).toEqual('yaml'); - expect(wrapper.find('CodeEditor').prop('value')).toEqual( - '---\na: &aa [a,b,c]\nb: *aa\n' - ); - }); - - it('should set Formik error if yaml is invalid', async () => { - const value = '---\nfoo bar\n'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - wrapper.find('Button').at(1).simulate('click'); - wrapper.update(); - - const field = wrapper.find('CodeEditor'); - expect(field.prop('hasErrors')).toEqual(true); - expect(wrapper.find('.pf-m-error')).toHaveLength(1); - }); - - it('should render tooltip', () => { - const value = '---\n'; - const wrapper = mountWithContexts( - - {() => ( - - )} - - ); - expect(wrapper.find('Popover[data-cy="the-field-tooltip"]').length).toBe(1); - }); - - it('should submit value through Formik', async () => { - const value = '---\nfoo: bar\n'; - const handleSubmit = jest.fn(); - const wrapper = mountWithContexts( - - {(formik) => ( -
- - - - )} -
- ); - await act(async () => { - wrapper.find('CodeEditor').invoke('onChange')('---\nnewval: changed'); - wrapper.find('form').simulate('submit'); - }); - - expect(handleSubmit).toHaveBeenCalled(); - expect(handleSubmit.mock.calls[0][0]).toEqual({ - variables: '---\nnewval: changed', - }); - }); - - it('should initialize to JSON if value is JSON', async () => { - const value = '{"foo": "bar"}'; - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - - {(formik) => ( -
- - - - )} -
- ); - }); - wrapper.update(); - - expect(wrapper.find('CodeEditor').prop('mode')).toEqual('javascript'); - }); - - it('should open modal when expanded', async () => { - const value = '---'; - const wrapper = mountWithContexts( - - {(formik) => ( -
- - - - )} -
- ); - expect(wrapper.find('Modal').prop('isOpen')).toEqual(false); - - wrapper.find('Button[variant="plain"]').invoke('onClick')(); - wrapper.update(); - - expect(wrapper.find('Modal').prop('isOpen')).toEqual(true); - expect(wrapper.find('Modal CodeEditor')).toHaveLength(1); - }); - - it('should format JSON for code editor', async () => { - const value = '{"foo": "bar"}'; - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - - {(formik) => ( -
- - - - )} -
- ); - }); - wrapper.update(); - - expect(wrapper.find('CodeEditor').prop('value')).toEqual( - '{\n "foo": "bar"\n}' - ); - }); -}); diff --git a/awx/ui/src/components/CodeEditor/constants.js b/awx/ui/src/components/CodeEditor/constants.js deleted file mode 100644 index 79f71887e2bf..000000000000 --- a/awx/ui/src/components/CodeEditor/constants.js +++ /dev/null @@ -1,2 +0,0 @@ -export const YAML_MODE = 'yaml'; -export const JSON_MODE = 'javascript'; diff --git a/awx/ui/src/components/CodeEditor/index.js b/awx/ui/src/components/CodeEditor/index.js deleted file mode 100644 index 0339137c2921..000000000000 --- a/awx/ui/src/components/CodeEditor/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import CodeEditor from './CodeEditor'; - -export default CodeEditor; -export { default as CodeEditorField } from './CodeEditorField'; -export { default as VariablesDetail } from './VariablesDetail'; -export { default as VariablesField } from './VariablesField'; diff --git a/awx/ui/src/components/ContentEmpty/ContentEmpty.js b/awx/ui/src/components/ContentEmpty/ContentEmpty.js deleted file mode 100644 index d74ee46dc610..000000000000 --- a/awx/ui/src/components/ContentEmpty/ContentEmpty.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { t } from '@lingui/macro'; -import { - Title, - EmptyState, - EmptyStateIcon, - EmptyStateBody, -} from '@patternfly/react-core'; -import { CubesIcon } from '@patternfly/react-icons'; - -const ContentEmpty = ({ - title = '', - message = '', - icon = CubesIcon, - className = '', -}) => ( - - - - {title || t`No items found.`} - - {message} - -); - -export { ContentEmpty as _ContentEmpty }; -export default ContentEmpty; diff --git a/awx/ui/src/components/ContentEmpty/ContentEmpty.test.js b/awx/ui/src/components/ContentEmpty/ContentEmpty.test.js deleted file mode 100644 index 6498dc76cc78..000000000000 --- a/awx/ui/src/components/ContentEmpty/ContentEmpty.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import ContentEmpty from './ContentEmpty'; - -describe('ContentEmpty', () => { - test('renders the expected content', () => { - const wrapper = mountWithContexts(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/ContentEmpty/index.js b/awx/ui/src/components/ContentEmpty/index.js deleted file mode 100644 index cdf62bc88112..000000000000 --- a/awx/ui/src/components/ContentEmpty/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ContentEmpty'; diff --git a/awx/ui/src/components/ContentError/ContentError.js b/awx/ui/src/components/ContentError/ContentError.js deleted file mode 100644 index 4be6a44042c9..000000000000 --- a/awx/ui/src/components/ContentError/ContentError.js +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-disable react/jsx-no-useless-fragment */ -import React from 'react'; -import { Link, Redirect } from 'react-router-dom'; -import { bool, instanceOf } from 'prop-types'; -import { t } from '@lingui/macro'; - -import { - Title, - EmptyState, - EmptyStateIcon, - EmptyStateBody, -} from '@patternfly/react-core'; -import { ExclamationTriangleIcon } from '@patternfly/react-icons'; -import { useSession } from 'contexts/Session'; -import ErrorDetail from '../ErrorDetail'; - -function ContentError({ error, children, isNotFound }) { - const { logout } = useSession(); - - if (error && error.response && error.response.status === 401) { - if (!error.response.headers['session-timeout']) { - logout(); - return null; - } - } - const is404 = - isNotFound || (error && error.response && error.response.status === 404); - const is401 = error && error.response && error.response.status === 401; - return ( - <> - {is401 ? ( - - ) : ( - - - - {is404 ? t`Not Found` : t`Something went wrong...`} - - - {is404 - ? t`The page you requested could not be found.` - : t`There was an error loading this content. Please reload the page.`}{' '} - {children || {t`Back to Dashboard.`}} - - {error && } - - )} - - ); -} -ContentError.propTypes = { - error: instanceOf(Error), - isNotFound: bool, -}; -ContentError.defaultProps = { - error: null, - isNotFound: false, -}; - -export { ContentError as _ContentError }; -export default ContentError; diff --git a/awx/ui/src/components/ContentError/ContentError.test.js b/awx/ui/src/components/ContentError/ContentError.test.js deleted file mode 100644 index 518f0ae8c65d..000000000000 --- a/awx/ui/src/components/ContentError/ContentError.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import ContentError from './ContentError'; - -describe('ContentError', () => { - test('renders the expected content', () => { - const wrapper = mountWithContexts( - - ); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/ContentError/index.js b/awx/ui/src/components/ContentError/index.js deleted file mode 100644 index 14587f410d50..000000000000 --- a/awx/ui/src/components/ContentError/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ContentError'; diff --git a/awx/ui/src/components/ContentLoading/ContentLoading.js b/awx/ui/src/components/ContentLoading/ContentLoading.js deleted file mode 100644 index 451ce2ba4bc2..000000000000 --- a/awx/ui/src/components/ContentLoading/ContentLoading.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -import styled from 'styled-components'; -import { - EmptyState as PFEmptyState, - EmptyStateIcon, - Spinner, -} from '@patternfly/react-core'; - -const EmptyState = styled(PFEmptyState)` - --pf-c-empty-state--m-lg--MaxWidth: none; - min-height: 250px; -`; - -// TODO: Better loading state - skeleton lines / spinner, etc. -const ContentLoading = ({ className }) => ( - - - -); - -export { ContentLoading as _ContentLoading }; -export default ContentLoading; diff --git a/awx/ui/src/components/ContentLoading/ContentLoading.test.js b/awx/ui/src/components/ContentLoading/ContentLoading.test.js deleted file mode 100644 index c2816e258272..000000000000 --- a/awx/ui/src/components/ContentLoading/ContentLoading.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import ContentLoading from './ContentLoading'; - -describe('ContentLoading', () => { - test('renders the expected content', () => { - const wrapper = mountWithContexts(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/ContentLoading/index.js b/awx/ui/src/components/ContentLoading/index.js deleted file mode 100644 index 0e87a3fb97db..000000000000 --- a/awx/ui/src/components/ContentLoading/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ContentLoading'; diff --git a/awx/ui/src/components/CopyButton/CopyButton.js b/awx/ui/src/components/CopyButton/CopyButton.js deleted file mode 100644 index 9ea5216f51ab..000000000000 --- a/awx/ui/src/components/CopyButton/CopyButton.js +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useEffect } from 'react'; -import { t } from '@lingui/macro'; -import PropTypes from 'prop-types'; -import { Button } from '@patternfly/react-core'; -import { CopyIcon } from '@patternfly/react-icons'; -import useRequest, { useDismissableError } from 'hooks/useRequest'; -import AlertModal from '../AlertModal'; -import ErrorDetail from '../ErrorDetail'; - -function CopyButton({ - id, - copyItem, - isDisabled, - onCopyStart, - onCopyFinish, - errorMessage, - ouiaId, -}) { - const { - isLoading, - error: copyError, - request: copyItemToAPI, - } = useRequest(copyItem); - - useEffect(() => { - if (isLoading) { - return onCopyStart(); - } - return onCopyFinish(); - }, [isLoading, onCopyStart, onCopyFinish]); - - const { error, dismissError } = useDismissableError(copyError); - - return ( - <> - - {error && ( - - {errorMessage} - - - )} - - ); -} - -CopyButton.propTypes = { - copyItem: PropTypes.func.isRequired, - onCopyStart: PropTypes.func.isRequired, - onCopyFinish: PropTypes.func.isRequired, - errorMessage: PropTypes.string.isRequired, - isDisabled: PropTypes.bool, - ouiaId: PropTypes.string, -}; - -CopyButton.defaultProps = { - isDisabled: false, - ouiaId: null, -}; - -export default CopyButton; diff --git a/awx/ui/src/components/CopyButton/CopyButton.test.js b/awx/ui/src/components/CopyButton/CopyButton.test.js deleted file mode 100644 index d1926945c4cf..000000000000 --- a/awx/ui/src/components/CopyButton/CopyButton.test.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import CopyButton from './CopyButton'; - -jest.mock('../../api'); - -let wrapper; - -describe('', () => { - test('should mount properly', async () => { - await act(async () => { - wrapper = mountWithContexts( - {}} - onCopyFinish={() => {}} - copyItem={() => {}} - errorMessage="Failed to copy template." - /> - ); - }); - expect(wrapper.find('CopyButton').length).toBe(1); - }); - - test('should call the correct function on button click', async () => { - const copyItem = jest.fn(); - await act(async () => { - wrapper = mountWithContexts( - {}} - onCopyFinish={() => {}} - copyItem={copyItem} - errorMessage="Failed to copy template." - /> - ); - }); - await act(async () => { - wrapper.find('button').simulate('click'); - }); - expect(copyItem).toHaveBeenCalledTimes(1); - }); -}); diff --git a/awx/ui/src/components/CopyButton/index.js b/awx/ui/src/components/CopyButton/index.js deleted file mode 100644 index 90e9e6d2043d..000000000000 --- a/awx/ui/src/components/CopyButton/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CopyButton'; diff --git a/awx/ui/src/components/CredentialChip/CredentialChip.js b/awx/ui/src/components/CredentialChip/CredentialChip.js deleted file mode 100644 index 43acf57fd048..000000000000 --- a/awx/ui/src/components/CredentialChip/CredentialChip.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; - -import { t } from '@lingui/macro'; -import { Chip } from '@patternfly/react-core'; -import { Credential } from 'types'; -import { toTitleCase } from 'util/strings'; - -function CredentialChip({ credential, ...props }) { - let type; - if (credential.cloud) { - type = t`Cloud`; - } else if (credential.kind === 'gpg_public_key') { - type = t`GPG Public Key`; - } else if (credential.kind === 'aws' || credential.kind === 'ssh') { - type = credential.kind.toUpperCase(); - } else { - type = toTitleCase(credential.kind); - } - - const buildCredentialName = () => { - if (credential.kind === 'vault' && credential.inputs?.vault_id) { - return `${credential.name} | ${credential.inputs.vault_id}`; - } - return `${credential.name}`; - }; - - return ( - - {type}: - {buildCredentialName()} - - ); -} -CredentialChip.propTypes = { - credential: Credential.isRequired, -}; - -export { CredentialChip as _CredentialChip }; -export default CredentialChip; diff --git a/awx/ui/src/components/CredentialChip/CredentialChip.test.js b/awx/ui/src/components/CredentialChip/CredentialChip.test.js deleted file mode 100644 index df678d5d23ce..000000000000 --- a/awx/ui/src/components/CredentialChip/CredentialChip.test.js +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import CredentialChip from './CredentialChip'; - -describe('CredentialChip', () => { - test('should render SSH kind', () => { - const credential = { - id: 1, - kind: 'ssh', - name: 'foo', - }; - - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('CredentialChip').text()).toEqual('SSH: foo'); - }); - - test('should render AWS kind', () => { - const credential = { - id: 1, - kind: 'aws', - name: 'foo', - }; - - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('CredentialChip').text()).toEqual('AWS: foo'); - }); - - test('should render with "Cloud"', () => { - const credential = { - id: 1, - cloud: true, - kind: 'other', - name: 'foo', - }; - - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('CredentialChip').text()).toEqual('Cloud: foo'); - }); - - test('should render with other kind', () => { - const credential = { - id: 1, - kind: 'other', - name: 'foo', - }; - - const wrapper = mountWithContexts( - - ); - expect(wrapper.find('CredentialChip').text()).toEqual('Other: foo'); - }); -}); diff --git a/awx/ui/src/components/CredentialChip/index.js b/awx/ui/src/components/CredentialChip/index.js deleted file mode 100644 index c4cb5cc38a55..000000000000 --- a/awx/ui/src/components/CredentialChip/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CredentialChip'; diff --git a/awx/ui/src/components/DataListToolbar/DataListToolbar.js b/awx/ui/src/components/DataListToolbar/DataListToolbar.js deleted file mode 100644 index 40deda10a151..000000000000 --- a/awx/ui/src/components/DataListToolbar/DataListToolbar.js +++ /dev/null @@ -1,248 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import PropTypes from 'prop-types'; -import styled from 'styled-components'; -import { t } from '@lingui/macro'; -import { - Button, - Checkbox, - Toolbar, - ToolbarContent as PFToolbarContent, - ToolbarGroup, - ToolbarItem, - ToolbarToggleGroup, - Tooltip, - Dropdown, - DropdownPosition, - KebabToggle, -} from '@patternfly/react-core'; -import { - AngleDownIcon, - AngleRightIcon, - SearchIcon, -} from '@patternfly/react-icons'; -import { SearchColumns, SortColumns, QSConfig, SearchableKeys } from 'types'; -import { KebabifiedProvider } from 'contexts/Kebabified'; -import ExpandCollapse from '../ExpandCollapse'; -import Search from '../Search'; -import Sort from '../Sort'; - -const ToolbarContent = styled(PFToolbarContent)` - & > .pf-c-toolbar__content-section { - flex-wrap: nowrap; - } -`; - -function DataListToolbar({ - isAllExpanded, - onExpandAll, - itemCount, - clearAllFilters, - searchColumns, - searchableKeys, - relatedSearchableKeys, - sortColumns, - isAllSelected, - onSelectAll, - isCompact, - onSort, - onSearch, - onReplaceSearch, - onRemove, - onCompact, - onExpand, - additionalControls, - qsConfig, - pagination, - enableNegativeFiltering, - enableRelatedFuzzyFiltering, - handleIsAnsibleFactsSelected, - isFilterCleared, -}) { - const showExpandCollapse = onCompact && onExpand; - const [isKebabOpen, setIsKebabOpen] = useState(false); - const [isKebabModalOpen, setIsKebabModalOpen] = useState(false); - const [isAdvancedSearchShown, setIsAdvancedSearchShown] = useState(false); - - const viewportWidth = - window.innerWidth || document.documentElement.clientWidth; - const dropdownPosition = - viewportWidth >= 992 ? DropdownPosition.right : DropdownPosition.left; - - const onShowAdvancedSearch = (shown) => { - setIsAdvancedSearchShown(shown); - setIsKebabOpen(false); - }; - - useEffect(() => { - if (!isKebabModalOpen) { - setIsKebabOpen(false); - } - }, [isKebabModalOpen]); - - const kebabProviderValue = useMemo( - () => ({ - isKebabified: true, - onKebabModalChange: setIsKebabModalOpen, - }), - [setIsKebabModalOpen] - ); - return ( - - - {onExpandAll && ( - - - - - - )} - {onSelectAll && ( - - - - - - - - )} - } breakpoint="lg"> - - - - {sortColumns && ( - - - - )} - - {showExpandCollapse && ( - - - - - - )} - {isAdvancedSearchShown && additionalControls.length > 0 && ( - - - { - if (!isKebabModalOpen) { - setIsKebabOpen(isOpen); - } - }} - /> - } - isOpen={isKebabOpen} - position={dropdownPosition} - isPlain - dropdownItems={additionalControls} - ouiaId="actions-dropdown" - /> - - - )} - {!isAdvancedSearchShown && ( - - {additionalControls.map((control) => ( - {control} - ))} - - )} - {!isAdvancedSearchShown && pagination && itemCount > 0 && ( - {pagination} - )} - - - ); -} - -DataListToolbar.propTypes = { - itemCount: PropTypes.number, - clearAllFilters: PropTypes.func, - qsConfig: QSConfig.isRequired, - searchColumns: SearchColumns.isRequired, - searchableKeys: SearchableKeys, - relatedSearchableKeys: PropTypes.arrayOf(PropTypes.string), - sortColumns: SortColumns, - isAllSelected: PropTypes.bool, - isCompact: PropTypes.bool, - onCompact: PropTypes.func, - onExpand: PropTypes.func, - onSearch: PropTypes.func, - onReplaceSearch: PropTypes.func, - onSelectAll: PropTypes.func, - onSort: PropTypes.func, - additionalControls: PropTypes.arrayOf(PropTypes.node), - enableNegativeFiltering: PropTypes.bool, - enableRelatedFuzzyFiltering: PropTypes.bool, -}; - -DataListToolbar.defaultProps = { - itemCount: 0, - searchableKeys: [], - relatedSearchableKeys: [], - sortColumns: null, - clearAllFilters: null, - isAllSelected: false, - isCompact: false, - onCompact: null, - onExpand: null, - onSearch: null, - onReplaceSearch: null, - onSelectAll: null, - onSort: null, - additionalControls: [], - enableNegativeFiltering: true, - enableRelatedFuzzyFiltering: true, -}; - -export default DataListToolbar; diff --git a/awx/ui/src/components/DataListToolbar/DataListToolbar.test.js b/awx/ui/src/components/DataListToolbar/DataListToolbar.test.js deleted file mode 100644 index 6ff3001740ca..000000000000 --- a/awx/ui/src/components/DataListToolbar/DataListToolbar.test.js +++ /dev/null @@ -1,346 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { shallow } from 'enzyme'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import DataListToolbar from './DataListToolbar'; -import AddDropDownButton from '../AddDropDownButton/AddDropDownButton'; - -describe('', () => { - let toolbar; - - const QS_CONFIG = { - namespace: 'organization', - dateFields: ['modified', 'created'], - defaultParams: { page: 1, page_size: 5, order_by: 'name' }, - integerFields: ['page', 'page_size'], - }; - - const onSearch = jest.fn(); - const onReplaceSearch = jest.fn(); - const onSort = jest.fn(); - const onSelectAll = jest.fn(); - const onExpandAll = jest.fn(); - - test('it triggers the expected callbacks', () => { - const searchColumns = [ - { name: 'Name', key: 'name__icontains', isDefault: true }, - ]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - const search = 'button[aria-label="Search submit button"]'; - const searchTextInput = 'input[aria-label="Search text input"]'; - const selectAll = 'input[aria-label="Select all"]'; - const sort = 'button[aria-label="Sort"]'; - - toolbar = mountWithContexts( - - ); - - toolbar.find(sort).simulate('click'); - toolbar.find(selectAll).simulate('change', { target: { checked: false } }); - - expect(onSelectAll).toHaveBeenCalledTimes(1); - expect(onSort).toHaveBeenCalledTimes(1); - expect(onSort).toBeCalledWith('name', 'descending'); - - expect(onSelectAll).toHaveBeenCalledTimes(1); - expect(onSelectAll.mock.calls[0][0]).toBe(false); - - toolbar.find(searchTextInput).instance().value = 'test-321'; - toolbar.find(searchTextInput).simulate('change'); - toolbar.find(search).simulate('click'); - - expect(onSearch).toHaveBeenCalledTimes(1); - expect(onSearch).toBeCalledWith('name__icontains', 'test-321'); - }); - - test('dropdown items sortable/searchable columns work', () => { - const sortDropdownToggleSelector = 'button[id="awx-sort"]'; - const searchDropdownToggleSelector = - 'Select[aria-label="Simple key select"] SelectToggle'; - const sortDropdownMenuItems = - 'DropdownMenu > ul[aria-labelledby="awx-sort"]'; - const searchDropdownMenuItems = - 'Select[aria-label="Simple key select"] SelectOption'; - - const NEW_QS_CONFIG = { - namespace: 'organization', - dateFields: ['modified', 'created'], - defaultParams: { page: 1, page_size: 5, order_by: 'foo' }, - integerFields: ['page', 'page_size'], - }; - - const searchColumns = [ - { name: 'Foo', key: 'foo', isDefault: true }, - { name: 'Bar', key: 'bar' }, - ]; - const sortColumns = [ - { name: 'Foo', key: 'foo' }, - { name: 'Bar', key: 'bar' }, - { name: 'Bakery', key: 'Bakery' }, - ]; - - toolbar = mountWithContexts( - {}} - /> - ); - const sortDropdownToggle = toolbar.find(sortDropdownToggleSelector); - expect(sortDropdownToggle.length).toBe(1); - sortDropdownToggle.simulate('click'); - toolbar.update(); - const sortDropdownItems = toolbar.find(sortDropdownMenuItems).children(); - expect(sortDropdownItems.length).toBe(2); - let searchDropdownToggle = toolbar.find(searchDropdownToggleSelector); - expect(searchDropdownToggle.length).toBe(1); - searchDropdownToggle.simulate('click'); - toolbar.update(); - let searchDropdownItems = toolbar.find(searchDropdownMenuItems).children(); - expect(searchDropdownItems.length).toBe(2); - const mockedSortEvent = { target: { innerText: 'Bar' } }; - searchDropdownItems.at(0).simulate('click', mockedSortEvent); - toolbar = mountWithContexts( - {}} - /> - ); - toolbar.update(); - - const sortDropdownToggleDescending = toolbar.find( - sortDropdownToggleSelector - ); - expect(sortDropdownToggleDescending.length).toBe(1); - sortDropdownToggleDescending.simulate('click'); - toolbar.update(); - - const sortDropdownItemsDescending = toolbar - .find(sortDropdownMenuItems) - .children(); - expect(sortDropdownItemsDescending.length).toBe(2); - sortDropdownToggleDescending.simulate('click'); // toggle close the sort dropdown - - const mockedSortEventDescending = { target: { innerText: 'Bar' } }; - sortDropdownItems.at(0).simulate('click', mockedSortEventDescending); - toolbar.update(); - - searchDropdownToggle = toolbar.find(searchDropdownToggleSelector); - expect(searchDropdownToggle.length).toBe(1); - searchDropdownToggle.simulate('click'); - toolbar.update(); - - searchDropdownItems = toolbar.find(searchDropdownMenuItems).children(); - expect(searchDropdownItems.length).toBe(2); - - const mockedSearchEvent = { target: { innerText: 'Bar' } }; - searchDropdownItems.at(0).simulate('click', mockedSearchEvent); - }); - - test('should render sort icon', () => { - const qsConfig = { - namespace: 'organization', - dateFields: ['modified', 'created'], - defaultParams: { page: 1, page_size: 5, order_by: 'id' }, - integerFields: ['page', 'page_size', 'id'], - }; - const sortColumns = [{ name: 'Name', key: 'name' }]; - - const wrapper = shallow( - - ); - - const sort = wrapper.find('Sort'); - expect(sort.prop('qsConfig')).toEqual(qsConfig); - expect(sort.prop('columns')).toEqual(sortColumns); - expect(sort.prop('onSort')).toEqual(onSort); - }); - - test('should render additionalControls', () => { - const searchColumns = [{ name: 'Name', key: 'name', isDefault: true }]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - - toolbar = mountWithContexts( - - click - , - ]} - /> - ); - - const button = toolbar.find('#test'); - expect(button).toHaveLength(1); - expect(button.text()).toEqual('click'); - }); - - test('it triggers the expected callbacks', () => { - const searchColumns = [{ name: 'Name', key: 'name', isDefault: true }]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - toolbar = mountWithContexts( - - ); - const checkbox = toolbar.find('Checkbox'); - expect(checkbox.prop('isChecked')).toBe(true); - }); - - test('always adds advanced item to search column array', () => { - const searchColumns = [{ name: 'Name', key: 'name', isDefault: true }]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - - toolbar = mountWithContexts( - - click - , - ]} - /> - ); - - const search = toolbar.find('Search'); - expect( - search.prop('columns').filter((col) => col.key === 'advanced').length - ).toBe(1); - }); - - test('should properly render toolbar buttons when in advanced search mode', async () => { - const searchColumns = [{ name: 'Name', key: 'name', isDefault: true }]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - - const newToolbar = mountWithContexts( - - Add Contaner -
, -
- Add Instance Group -
, - ]} - />, - ]} - /> - ); - act(() => newToolbar.find('Search').prop('onShowAdvancedSearch')(true)); - newToolbar.update(); - expect(newToolbar.find('KebabToggle').length).toBe(1); - act(() => newToolbar.find('KebabToggle').prop('onToggle')(true)); - newToolbar.update(); - expect(newToolbar.find('div[aria-label="add container"]').length).toBe(1); - expect(newToolbar.find('div[aria-label="add instance group"]').length).toBe( - 1 - ); - }); - - test('should handle expanded rows', async () => { - const searchColumns = [ - { name: 'Name', key: 'name__icontains', isDefault: true }, - ]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - - const newtoolbar = mountWithContexts( - - ); - await act(async () => - newtoolbar.find('Button[aria-label="Expand all rows"]').prop('onClick')() - ); - expect(newtoolbar.find('AngleRightIcon')).toHaveLength(1); - expect(newtoolbar.find('AngleDownIcon')).toHaveLength(0); - expect(onExpandAll).toBeCalledWith(true); - }); - - test('should render angle down icon', async () => { - const searchColumns = [ - { name: 'Name', key: 'name__icontains', isDefault: true }, - ]; - const sortColumns = [{ name: 'Name', key: 'name' }]; - - const newtoolbar = mountWithContexts( - - ); - - expect(newtoolbar.find('AngleDownIcon')).toHaveLength(1); - expect(newtoolbar.find('AngleRightIcon')).toHaveLength(0); - }); -}); diff --git a/awx/ui/src/components/DataListToolbar/index.js b/awx/ui/src/components/DataListToolbar/index.js deleted file mode 100644 index 038f0690174c..000000000000 --- a/awx/ui/src/components/DataListToolbar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DataListToolbar'; diff --git a/awx/ui/src/components/DeleteButton/DeleteButton.js b/awx/ui/src/components/DeleteButton/DeleteButton.js deleted file mode 100644 index a3e4b0ad9455..000000000000 --- a/awx/ui/src/components/DeleteButton/DeleteButton.js +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; -import styled from 'styled-components'; -import { Button, Badge, Alert, Tooltip } from '@patternfly/react-core'; -import { getRelatedResourceDeleteCounts } from 'util/getRelatedResourceDeleteDetails'; -import AlertModal from '../AlertModal'; -import ErrorDetail from '../ErrorDetail'; - -const WarningMessage = styled(Alert)` - margin-top: 10px; -`; -const Label = styled.span` - && { - margin-right: 10px; - } -`; -function DeleteButton({ - onConfirm, - modalTitle, - name, - variant, - children, - isDisabled, - ouiaId, - deleteMessage, - deleteDetailsRequests, - disabledTooltip, -}) { - const [isOpen, setIsOpen] = useState(false); - const [deleteMessageError, setDeleteMessageError] = useState(); - const [deleteDetails, setDeleteDetails] = useState({}); - const [isLoading, setIsLoading] = useState(false); - - const toggleModal = async (isModalOpen) => { - setIsLoading(true); - if (deleteDetailsRequests?.length && isModalOpen) { - const { results, error } = await getRelatedResourceDeleteCounts( - deleteDetailsRequests - ); - if (error) { - setDeleteMessageError(error); - } else { - setDeleteDetails(results); - } - } - setIsLoading(false); - setIsOpen(isModalOpen); - }; - - if (deleteMessageError) { - return ( - { - toggleModal(false); - setDeleteMessageError(); - }} - > - - - ); - } - return ( - <> - {disabledTooltip ? ( - -
- -
-
- ) : ( - - )} - toggleModal(false)} - actions={[ - , - , - ]} - > - {t`Are you sure you want to delete:`} -
- {name} - {Object.values(deleteDetails).length > 0 && ( - -
{deleteMessage}
-
- {Object.entries(deleteDetails).map(([key, value]) => ( -
- {value} -
- ))} -
- } - /> - )} - - - ); -} - -DeleteButton.propTypes = { - ouiaId: PropTypes.string, -}; - -DeleteButton.defaultProps = { - ouiaId: null, -}; - -export default DeleteButton; diff --git a/awx/ui/src/components/DeleteButton/DeleteButton.test.js b/awx/ui/src/components/DeleteButton/DeleteButton.test.js deleted file mode 100644 index 12744cfe5bea..000000000000 --- a/awx/ui/src/components/DeleteButton/DeleteButton.test.js +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { CredentialsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../testUtils/enzymeHelpers'; -import DeleteButton from './DeleteButton'; - -jest.mock('../../api'); - -describe('', () => { - test('should render button', () => { - const wrapper = mountWithContexts( - {}} name="Foo" /> - ); - expect(wrapper.find('button')).toHaveLength(1); - }); - - test('should open confirmation modal', async () => { - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - {}} - name="Foo" - deleteDetailsRequests={[ - { - label: 'job', - request: CredentialsAPI.read.mockResolvedValue({ - data: { count: 1 }, - }), - }, - ]} - deleteMessage="Delete this?" - warningMessage="Are you sure to want to delete this" - /> - ); - }); - - await act(async () => { - wrapper.find('button').prop('onClick')(); - }); - - await waitForElement(wrapper, 'Modal', (el) => el.length > 0); - expect(wrapper.find('Modal')).toHaveLength(1); - - expect(wrapper.find('div[aria-label="Delete this?"]')).toHaveLength(1); - }); - - test('should invoke onConfirm prop', async () => { - const onConfirm = jest.fn(); - const wrapper = mountWithContexts( - - ); - await act(async () => wrapper.find('button').simulate('click')); - wrapper.update(); - await act(async () => - wrapper - .find('ModalBoxFooter button[aria-label="Confirm Delete"]') - .simulate('click') - ); - wrapper.update(); - expect(onConfirm).toHaveBeenCalled(); - }); - - test('should show delete details error', async () => { - const onConfirm = jest.fn(); - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - await act(async () => wrapper.find('button').simulate('click')); - wrapper.update(); - - expect(wrapper.find('AlertModal[title="Error!"]')).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/DeleteButton/index.js b/awx/ui/src/components/DeleteButton/index.js deleted file mode 100644 index 991ad79b298d..000000000000 --- a/awx/ui/src/components/DeleteButton/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DeleteButton'; diff --git a/awx/ui/src/components/DetailList/ArrayDetail.js b/awx/ui/src/components/DetailList/ArrayDetail.js deleted file mode 100644 index 619217b50b0a..000000000000 --- a/awx/ui/src/components/DetailList/ArrayDetail.js +++ /dev/null @@ -1,37 +0,0 @@ -import 'styled-components/macro'; -import React from 'react'; -import styled from 'styled-components'; -import { TextListItemVariants } from '@patternfly/react-core'; -import { DetailName, DetailValue } from './Detail'; -import Popover from '../Popover'; - -const Value = styled(DetailValue)` - margin-top: var(--pf-global--spacer--xs); - padding: var(--pf-global--spacer--xs); - border: 1px solid var(--pf-global--BorderColor--100); - max-height: 5.5em; - overflow: auto; -`; - -function ArrayDetail({ label, helpText, value, dataCy }) { - const labelCy = dataCy ? `${dataCy}-label` : null; - const valueCy = dataCy ? `${dataCy}-value` : null; - - const vals = Array.isArray(value) ? value : [value]; - - return ( -
- - {label} - {helpText && } - - - {vals.map((v) => ( -
{v}
- ))} -
-
- ); -} - -export default ArrayDetail; diff --git a/awx/ui/src/components/DetailList/CodeDetail.js b/awx/ui/src/components/DetailList/CodeDetail.js deleted file mode 100644 index 5e926c6f6481..000000000000 --- a/awx/ui/src/components/DetailList/CodeDetail.js +++ /dev/null @@ -1,75 +0,0 @@ -import 'styled-components/macro'; -import React from 'react'; -import { - arrayOf, - oneOf, - oneOfType, - node, - number, - shape, - string, -} from 'prop-types'; -import { TextListItemVariants } from '@patternfly/react-core'; -import { DetailName, DetailValue } from './Detail'; -import CodeEditor from '../CodeEditor'; -import Popover from '../Popover'; - -function CodeDetail({ value, label, mode, rows, helpText, dataCy }) { - const labelCy = dataCy ? `${dataCy}-label` : null; - const valueCy = dataCy ? `${dataCy}-value` : null; - const editorId = dataCy ? `${dataCy}-editor` : 'code-editor'; - - return ( - <> - -
- - {helpText && ( - - )} -
-
- - - - - ); -} -CodeDetail.propTypes = { - value: oneOfType([shape({}), arrayOf(string), string]).isRequired, - label: node.isRequired, - dataCy: string, - helpText: string, - rows: oneOfType([number, string]), - mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired, -}; -CodeDetail.defaultProps = { - rows: null, - helpText: '', - dataCy: '', -}; - -export default CodeDetail; diff --git a/awx/ui/src/components/DetailList/DeletedDetail.js b/awx/ui/src/components/DetailList/DeletedDetail.js deleted file mode 100644 index ec0961d3948f..000000000000 --- a/awx/ui/src/components/DetailList/DeletedDetail.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; - -import { t } from '@lingui/macro'; -import { node } from 'prop-types'; -import styled from 'styled-components'; -import _Detail from './Detail'; - -const Detail = styled(_Detail)` - dd& { - color: red; - } -`; - -function DeletedDetail({ label, dataCy, helpText }) { - return ( - - ); -} - -DeletedDetail.propTypes = { - label: node.isRequired, -}; - -export default DeletedDetail; diff --git a/awx/ui/src/components/DetailList/Detail.js b/awx/ui/src/components/DetailList/Detail.js deleted file mode 100644 index 8f56f4ad40ae..000000000000 --- a/awx/ui/src/components/DetailList/Detail.js +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; - -import { oneOfType, node, bool, string } from 'prop-types'; -import { TextListItem, TextListItemVariants } from '@patternfly/react-core'; -import styled from 'styled-components'; -import Popover from '../Popover'; - -const DetailName = styled(({ fullWidth, ...props }) => ( - -))` - font-weight: var(--pf-global--FontWeight--bold); - ${(props) => - props.fullWidth && - ` - grid-column: 1; - `} -`; - -const DetailValue = styled( - ({ fullWidth, isEncrypted, isNotConfigured, ...props }) => ( - - ) -)` - overflow-wrap: break-word; - ${(props) => - props.fullWidth && - ` - grid-column: 2 / -1; - `} - ${(props) => - (props.isEncrypted || props.isNotConfigured) && - ` - color: var(--pf-global--disabled-color--100); - `} -`; - -const Detail = ({ - label, - value, - fullWidth, - className, - dataCy, - alwaysVisible, - isEmpty, - helpText, - isEncrypted, - isNotConfigured, -}) => { - if (!value && typeof value !== 'number' && !alwaysVisible) { - return null; - } - - if (isEmpty && !alwaysVisible) { - return null; - } - - const labelCy = dataCy ? `${dataCy}-label` : null; - const valueCy = dataCy ? `${dataCy}-value` : null; - - return ( - <> - - {label} - {helpText && } - - - {value} - - - ); -}; -Detail.propTypes = { - label: node.isRequired, - value: node, - fullWidth: bool, - alwaysVisible: bool, - helpText: oneOfType([string, node]), -}; -Detail.defaultProps = { - value: null, - fullWidth: false, - alwaysVisible: false, - helpText: null, -}; - -export default Detail; -export { DetailName }; -export { DetailValue }; diff --git a/awx/ui/src/components/DetailList/Detail.test.js b/awx/ui/src/components/DetailList/Detail.test.js deleted file mode 100644 index d78cf285668e..000000000000 --- a/awx/ui/src/components/DetailList/Detail.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; - -import Detail from './Detail'; - -describe('Detail', () => { - test('renders the expected content', () => { - const wrapper = mount(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/DetailList/DetailBadge.js b/awx/ui/src/components/DetailList/DetailBadge.js deleted file mode 100644 index 1482ae4adc07..000000000000 --- a/awx/ui/src/components/DetailList/DetailBadge.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { node } from 'prop-types'; -import styled from 'styled-components'; -import { Badge } from '@patternfly/react-core'; - -import _Detail from './Detail'; - -const Detail = styled(_Detail)` - word-break: break-word; -`; - -function DetailBadge({ label, helpText, content, dataCy = null }) { - return ( - {content}} - /> - ); -} -DetailBadge.propTypes = { - label: node.isRequired, - content: node.isRequired, -}; - -export default DetailBadge; diff --git a/awx/ui/src/components/DetailList/DetailList.js b/awx/ui/src/components/DetailList/DetailList.js deleted file mode 100644 index dceaa1cba947..000000000000 --- a/awx/ui/src/components/DetailList/DetailList.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { TextList, TextListVariants } from '@patternfly/react-core'; -import styled from 'styled-components'; - -const DetailList = ({ children, stacked, ...props }) => ( - - {children} - -); - -export default styled(DetailList)` - display: grid; - align-items: start; - ${(props) => (props.compact ? `column-gap: 20px;` : `grid-gap: 20px;`)} - ${(props) => - props.stacked - ? ` - grid-template-columns: auto 1fr; - ` - : ` - --column-count: 1; - grid-template-columns: repeat(var(--column-count), auto minmax(10em, 1fr)); - - @media (min-width: 920px) { - --column-count: 2; - } - - @media (min-width: 1210px) { - --column-count: 3; - } - `} - - & + & { - margin-top: 20px; - } -`; diff --git a/awx/ui/src/components/DetailList/DetailList.test.js b/awx/ui/src/components/DetailList/DetailList.test.js deleted file mode 100644 index 5e41f75de6ec..000000000000 --- a/awx/ui/src/components/DetailList/DetailList.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; - -import DetailList from './DetailList'; - -describe('DetailList', () => { - test('renders the expected content', () => { - const wrapper = mount(); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/DetailList/LaunchedByDetail.js b/awx/ui/src/components/DetailList/LaunchedByDetail.js deleted file mode 100644 index 6f542231c527..000000000000 --- a/awx/ui/src/components/DetailList/LaunchedByDetail.js +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { t } from '@lingui/macro'; -import getScheduleUrl from 'util/getScheduleUrl'; -import Detail from './Detail'; - -const getLaunchedByDetails = (job) => { - const { - created_by: createdBy, - job_template: jobTemplate, - workflow_job_template: workflowJT, - schedule, - } = job.summary_fields; - - if (!createdBy && !schedule) { - return {}; - } - - let link; - let value; - - switch (job.launch_type) { - case 'webhook': - value = t`Webhook`; - link = - (jobTemplate && `/templates/job_template/${jobTemplate.id}/details`) || - (workflowJT && - `/templates/workflow_job_template/${workflowJT.id}/details`); - break; - case 'scheduled': - value = schedule.name; - link = getScheduleUrl(job); - break; - case 'manual': - link = `/users/${createdBy.id}/details`; - value = createdBy.username; - break; - default: - link = createdBy && `/users/${createdBy.id}/details`; - value = createdBy && createdBy.username; - break; - } - - return { link, value }; -}; - -export default function LaunchedByDetail({ job, dataCy = null }) { - const { value: launchedByValue, link: launchedByLink } = - getLaunchedByDetails(job) || {}; - - return ( - {launchedByValue} - ) : ( - launchedByValue - ) - } - /> - ); -} diff --git a/awx/ui/src/components/DetailList/UserDateDetail.js b/awx/ui/src/components/DetailList/UserDateDetail.js deleted file mode 100644 index 407fb6d7eea3..000000000000 --- a/awx/ui/src/components/DetailList/UserDateDetail.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { node, string } from 'prop-types'; -import { Trans } from '@lingui/macro'; -import { Link } from 'react-router-dom'; -import styled from 'styled-components'; -import { formatDateString } from 'util/dates'; -import { SummaryFieldUser } from 'types'; -import _Detail from './Detail'; - -const Detail = styled(_Detail)` - word-break: break-word; -`; - -function UserDateDetail({ label, date, user }) { - const dateStr = formatDateString(date); - const username = user ? user.username : ''; - return ( - - {dateStr} by {username} - - ) : ( - dateStr - ) - } - /> - ); -} -UserDateDetail.propTypes = { - label: node.isRequired, - date: string.isRequired, - user: SummaryFieldUser, -}; -UserDateDetail.defaultProps = { - user: null, -}; - -export default UserDateDetail; diff --git a/awx/ui/src/components/DetailList/index.js b/awx/ui/src/components/DetailList/index.js deleted file mode 100644 index a393fe72a027..000000000000 --- a/awx/ui/src/components/DetailList/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export { default as DetailList } from './DetailList'; -export { default as Detail, DetailName, DetailValue } from './Detail'; -export { default as DeletedDetail } from './DeletedDetail'; -export { default as UserDateDetail } from './UserDateDetail'; -export { default as DetailBadge } from './DetailBadge'; -export { default as ArrayDetail } from './ArrayDetail'; -export { default as LaunchedByDetail } from './LaunchedByDetail'; -/* - NOTE: CodeDetail cannot be imported here, as it causes circular - dependencies in testing environment. Import it directly from - DetailList/ObjectDetail -*/ diff --git a/awx/ui/src/components/DisassociateButton/DisassociateButton.js b/awx/ui/src/components/DisassociateButton/DisassociateButton.js deleted file mode 100644 index 9b0c8278b136..000000000000 --- a/awx/ui/src/components/DisassociateButton/DisassociateButton.js +++ /dev/null @@ -1,188 +0,0 @@ -import React, { useState, useEffect, useContext } from 'react'; -import { arrayOf, func, shape, string, oneOfType, number } from 'prop-types'; - -import { t } from '@lingui/macro'; -import { Button, Tooltip, DropdownItem } from '@patternfly/react-core'; -import styled from 'styled-components'; -import { KebabifiedContext } from 'contexts/Kebabified'; - -import AlertModal from '../AlertModal'; - -const ModalNote = styled.div` - margin-bottom: var(--pf-global--spacer--xl); -`; - -function DisassociateButton({ - itemsToDisassociate = [], - modalNote = '', - modalTitle = t`Disassociate?`, - onDisassociate, - verifyCannotDisassociate = true, - isProtectedInstanceGroup = false, -}) { - const [isOpen, setIsOpen] = useState(false); - const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext); - - const handleDisassociate = () => { - onDisassociate(); - setIsOpen(false); - }; - - useEffect(() => { - if (isKebabified) { - onKebabModalChange(isOpen); - } - }, [isKebabified, isOpen, onKebabModalChange]); - - function cannotDisassociateAllOthers(item) { - return !item.summary_fields?.user_capabilities?.delete; - } - function cannotDisassociateInstances(item) { - return ( - item.node_type === 'control' || - (isProtectedInstanceGroup && item.node_type === 'hybrid') - ); - } - - const cannotDisassociate = itemsToDisassociate.some( - (i) => i.type === 'instance' - ) - ? cannotDisassociateInstances - : cannotDisassociateAllOthers; - - function renderTooltip() { - if (verifyCannotDisassociate) { - const itemsUnableToDisassociate = itemsToDisassociate - .filter(cannotDisassociate) - .map((item) => item.name ?? item.hostname) - .join(', '); - if ( - cannotDisassociate - ? itemsToDisassociate.some(cannotDisassociateInstances) - : itemsToDisassociate.some(cannotDisassociateAllOthers) - ) { - return ( -
- {t`You do not have permission to disassociate the following: ${itemsUnableToDisassociate}`} -
- ); - } - } - - if (itemsToDisassociate.length) { - return t`Disassociate`; - } - return t`Select a row to disassociate`; - } - - let isDisabled = false; - if (verifyCannotDisassociate) { - isDisabled = itemsToDisassociate.some(cannotDisassociate); - } - - // NOTE: Once PF supports tooltips on disabled elements, - // we can delete the extra
around the below. - // See: https://github.com/patternfly/patternfly-react/issues/1894 - return ( - <> - {isKebabified ? ( - setIsOpen(true)} - > - {t`Disassociate`} - - ) : ( - -
- -
-
- )} - - {isOpen && ( - setIsOpen(false)} - actions={[ - , - , - ]} - > - {modalNote && {modalNote}} - -
{t`This action will disassociate the following:`}
- - {itemsToDisassociate.map((item) => ( - - {item.hostname ? item.hostname : item.name} -
-
- ))} -
- )} - - ); -} - -DisassociateButton.defaultProps = { - itemsToDisassociate: [], - modalNote: '', - modalTitle: '', -}; - -DisassociateButton.propTypes = { - itemsToDisassociate: oneOfType([ - arrayOf( - shape({ - id: number.isRequired, - name: string.isRequired, - }) - ), - arrayOf( - shape({ - id: number.isRequired, - hostname: string.isRequired, - }) - ), - ]), - modalNote: string, - modalTitle: string, - onDisassociate: func.isRequired, -}; - -export default DisassociateButton; diff --git a/awx/ui/src/components/DisassociateButton/DisassociateButton.test.js b/awx/ui/src/components/DisassociateButton/DisassociateButton.test.js deleted file mode 100644 index 6a1cf5b040da..000000000000 --- a/awx/ui/src/components/DisassociateButton/DisassociateButton.test.js +++ /dev/null @@ -1,144 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import DisassociateButton from './DisassociateButton'; - -describe('', () => { - describe('User has disassociate permissions', () => { - let wrapper; - const handleDisassociate = jest.fn(); - const mockHosts = [ - { - id: 1, - name: 'foo', - summary_fields: { - user_capabilities: { - delete: true, - }, - }, - }, - { - id: 2, - name: 'bar', - summary_fields: { - user_capabilities: { - delete: true, - }, - }, - }, - ]; - - beforeAll(() => { - wrapper = mountWithContexts( - - ); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - test('should render button', () => { - expect(wrapper.find('button')).toHaveLength(1); - expect(wrapper.find('button').text()).toEqual('Disassociate'); - }); - - test('should open confirmation modal', () => { - wrapper.find('button').simulate('click'); - expect(wrapper.find('AlertModal')).toHaveLength(1); - }); - - test('cancel button should close confirmation modal', () => { - expect(wrapper.find('AlertModal')).toHaveLength(1); - wrapper.find('button[aria-label="Cancel"]').simulate('click'); - expect(wrapper.find('AlertModal')).toHaveLength(0); - }); - - test('should render expected modal content', () => { - wrapper.find('button').simulate('click'); - expect( - wrapper - .find('AlertModal') - .containsMatchingElement(
custom note
) - ).toEqual(true); - expect( - wrapper - .find('AlertModal') - .containsMatchingElement( -
This action will disassociate the following:
- ) - ).toEqual(true); - expect(wrapper.find('Title').text()).toEqual('custom title'); - wrapper.find('button[aria-label="Close"]').simulate('click'); - }); - - test('disassociate button should call handleDisassociate on click', () => { - wrapper.find('button').simulate('click'); - expect(handleDisassociate).toHaveBeenCalledTimes(0); - wrapper - .find('button[aria-label="confirm disassociate"]') - .simulate('click'); - expect(handleDisassociate).toHaveBeenCalledTimes(1); - }); - }); - - describe('User does not have disassociate permissions', () => { - const readOnlyHost = [ - { - id: 1, - name: 'foo', - summary_fields: { - user_capabilities: { - delete: false, - }, - }, - }, - ]; - - test('should disable button when no delete permissions', () => { - const wrapper = mountWithContexts( - {}} - itemsToDelete={readOnlyHost} - /> - ); - expect(wrapper.find('button[disabled]')).toHaveLength(1); - }); - - test('should disable button for control instance', () => { - const wrapper = mountWithContexts( - {}} - itemsToDelete={[ - { - id: 1, - hostname: 'awx', - node_type: 'control', - }, - ]} - /> - ); - expect(wrapper.find('button[disabled]')).toHaveLength(1); - }); - test('should disable button when selected items contain instances thaat are hybrid and are inside a protected instances', () => { - const wrapper = mountWithContexts( - {}} - isProectedInstanceGroup - itemsToDelete={[ - { - id: 1, - hostname: 'awx', - node_type: 'control', - }, - ]} - /> - ); - expect(wrapper.find('button[disabled]')).toHaveLength(1); - }); - }); -}); diff --git a/awx/ui/src/components/DisassociateButton/index.js b/awx/ui/src/components/DisassociateButton/index.js deleted file mode 100644 index c64669bc235a..000000000000 --- a/awx/ui/src/components/DisassociateButton/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DisassociateButton'; diff --git a/awx/ui/src/components/DocsLink/DocsLink.js b/awx/ui/src/components/DocsLink/DocsLink.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/awx/ui/src/components/DocsLink/index.js b/awx/ui/src/components/DocsLink/index.js deleted file mode 100644 index e1344df9d2e9..000000000000 --- a/awx/ui/src/components/DocsLink/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './DocsLink'; diff --git a/awx/ui/src/components/ErrorDetail/ErrorDetail.js b/awx/ui/src/components/ErrorDetail/ErrorDetail.js deleted file mode 100644 index 4f24e93d62f3..000000000000 --- a/awx/ui/src/components/ErrorDetail/ErrorDetail.js +++ /dev/null @@ -1,108 +0,0 @@ -import 'styled-components/macro'; -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import styled from 'styled-components'; - -import { t } from '@lingui/macro'; - -import { - Card as PFCard, - CardBody as PFCardBody, - ExpandableSection as PFExpandable, -} from '@patternfly/react-core'; -import getErrorMessage from './getErrorMessage'; - -const Card = styled(PFCard)` - background-color: var(--pf-global--BackgroundColor--200); - overflow-wrap: break-word; -`; - -const CardBody = styled(PFCardBody)` - max-height: 200px; - overflow: scroll; -`; - -const Expandable = styled(PFExpandable)` - text-align: left; - max-width: 75vw; - - & .pf-c-expandable__toggle { - padding-left: 10px; - margin-left: 5px; - margin-top: 10px; - margin-bottom: 10px; - } -`; - -function ErrorDetail({ error }) { - const { response } = error; - const [isExpanded, setIsExpanded] = useState(false); - - if (!error) { - return null; - } - - const handleToggle = () => { - setIsExpanded(!isExpanded); - }; - - const renderNetworkError = () => { - const message = getErrorMessage(response); - - return ( - <> - - {response?.config?.method.toUpperCase()} {response?.config?.url}{' '} - {response?.status} - - - {Array.isArray(message) ? ( -
    - {message.map((m) => - typeof m === 'string' ?
  • {m}
  • : null - )} -
- ) : ( - message - )} -
- - ); - }; - - const renderStack = () => ( - <> - - - {error.name}: {error.message} - - - - {error.stack} - - - ); - - return ( - - - {Object.prototype.hasOwnProperty.call(error, 'response') - ? renderNetworkError() - : renderStack()} - - - ); -} - -ErrorDetail.propTypes = { - error: PropTypes.instanceOf(Error), -}; -ErrorDetail.defaultProps = { - error: null, -}; - -export default ErrorDetail; diff --git a/awx/ui/src/components/ErrorDetail/ErrorDetail.test.js b/awx/ui/src/components/ErrorDetail/ErrorDetail.test.js deleted file mode 100644 index 1816f4a2dd78..000000000000 --- a/awx/ui/src/components/ErrorDetail/ErrorDetail.test.js +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import ErrorDetail from './ErrorDetail'; - -describe('ErrorDetail', () => { - test('renders the expected content', () => { - const wrapper = mountWithContexts( - - ); - expect(wrapper).toHaveLength(1); - }); - test('testing errors', () => { - const wrapper = mountWithContexts( - - ); - act(() => wrapper.find('ExpandableSection').prop('onToggle')()); - wrapper.update(); - }); -}); diff --git a/awx/ui/src/components/ErrorDetail/getErrorMessage.js b/awx/ui/src/components/ErrorDetail/getErrorMessage.js deleted file mode 100644 index 1c2450a64d75..000000000000 --- a/awx/ui/src/components/ErrorDetail/getErrorMessage.js +++ /dev/null @@ -1,15 +0,0 @@ -export default function getErrorMessage(response) { - if (!response?.data) { - return null; - } - if (typeof response.data === 'string') { - return response.data; - } - if (response.data.detail) { - return response.data.detail; - } - return Object.values(response.data).reduce( - (acc, currentValue) => acc.concat(currentValue), - [] - ); -} diff --git a/awx/ui/src/components/ErrorDetail/getErrorMessage.test.js b/awx/ui/src/components/ErrorDetail/getErrorMessage.test.js deleted file mode 100644 index c67728f00bc8..000000000000 --- a/awx/ui/src/components/ErrorDetail/getErrorMessage.test.js +++ /dev/null @@ -1,60 +0,0 @@ -import getErrorMessage from './getErrorMessage'; - -describe('getErrorMessage', () => { - test('should return data string', () => { - const response = { - data: 'error response', - }; - expect(getErrorMessage(response)).toEqual('error response'); - }); - test('should return detail string', () => { - const response = { - data: { - detail: 'detail string', - }, - }; - expect(getErrorMessage(response)).toEqual('detail string'); - }); - test('should return an array of strings', () => { - const response = { - data: { - project: ['project error response'], - }, - }; - expect(getErrorMessage(response)).toEqual(['project error response']); - }); - test('should consolidate error messages from multiple keys into an array', () => { - const response = { - data: { - project: ['project error response'], - inventory: ['inventory error response'], - organization: ['org error response'], - }, - }; - expect(getErrorMessage(response)).toEqual([ - 'project error response', - 'inventory error response', - 'org error response', - ]); - }); - test('should handle no response.data', () => { - const response = {}; - expect(getErrorMessage(response)).toEqual(null); - }); - test('should consolidate multiple error messages from multiple keys into an array', () => { - const response = { - data: { - project: ['project error response'], - inventory: [ - 'inventory error response', - 'another inventory error response', - ], - }, - }; - expect(getErrorMessage(response)).toEqual([ - 'project error response', - 'inventory error response', - 'another inventory error response', - ]); - }); -}); diff --git a/awx/ui/src/components/ErrorDetail/index.js b/awx/ui/src/components/ErrorDetail/index.js deleted file mode 100644 index 0f380db7abaa..000000000000 --- a/awx/ui/src/components/ErrorDetail/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ErrorDetail'; diff --git a/awx/ui/src/components/ExecutionEnvironmentDetail/ExecutionEnvironmentDetail.js b/awx/ui/src/components/ExecutionEnvironmentDetail/ExecutionEnvironmentDetail.js deleted file mode 100644 index f4029a19bd43..000000000000 --- a/awx/ui/src/components/ExecutionEnvironmentDetail/ExecutionEnvironmentDetail.js +++ /dev/null @@ -1,145 +0,0 @@ -import React from 'react'; -import { bool, string } from 'prop-types'; -import { Link } from 'react-router-dom'; -import { t, Trans } from '@lingui/macro'; -import { Popover, Tooltip } from '@patternfly/react-core'; -import styled from 'styled-components'; -import { ExclamationTriangleIcon as PFExclamationTriangleIcon } from '@patternfly/react-icons'; -import { ExecutionEnvironment } from 'types'; -import getDocsBaseUrl from 'util/getDocsBaseUrl'; -import { useConfig } from 'contexts/Config'; -import { Detail } from '../DetailList'; - -const ExclamationTriangleIcon = styled(PFExclamationTriangleIcon)` - color: var(--pf-global--warning-color--100); - margin-left: 18px; - cursor: pointer; -`; - -const ExclamationTrianglePopover = styled(PFExclamationTriangleIcon)` - color: var(--pf-global--warning-color--100); - margin-left: 18px; - cursor: pointer; -`; - -ExclamationTrianglePopover.displayName = 'ExclamationTrianglePopover'; - -function ExecutionEnvironmentDetail({ - executionEnvironment, - isDefaultEnvironment, - virtualEnvironment, - verifyMissingVirtualEnv, - helpText, - dataCy, -}) { - const config = useConfig(); - const docsLink = `${getDocsBaseUrl( - config - )}/html/upgrade-migration-guide/upgrade_to_ees.html`; - const label = isDefaultEnvironment - ? t`Default Execution Environment` - : t`Execution Environment`; - - if (executionEnvironment) { - return ( - - {executionEnvironment.name} - - } - helpText={helpText} - dataCy={dataCy} - /> - ); - } - if (verifyMissingVirtualEnv && virtualEnvironment && !executionEnvironment) { - return ( - - {t`Missing resource`} - - {t`Execution Environment Missing`}
} - bodyContent={ -
- - Custom virtual environment {virtualEnvironment} must be - replaced by an execution environment. For more information - about migrating to execution environments see{' '} - - the documentation. - - -
- } - position="right" - > - - - - - } - dataCy={`missing-${dataCy}`} - /> - ); - } - if ( - !verifyMissingVirtualEnv && - !virtualEnvironment && - !executionEnvironment - ) { - return ( - - {t`Missing resource`} - - - - - - - } - dataCy={dataCy} - /> - ); - } - - return null; -} - -ExecutionEnvironmentDetail.propTypes = { - executionEnvironment: ExecutionEnvironment, - isDefaultEnvironment: bool, - virtualEnvironment: string, - verifyMissingVirtualEnv: bool, - helpText: string, - dataCy: string, -}; - -ExecutionEnvironmentDetail.defaultProps = { - isDefaultEnvironment: false, - executionEnvironment: null, - virtualEnvironment: '', - verifyMissingVirtualEnv: true, - helpText: '', - dataCy: 'execution-environment-detail', -}; - -export default ExecutionEnvironmentDetail; diff --git a/awx/ui/src/components/ExecutionEnvironmentDetail/ExecutionEnvironmentDetail.test.js b/awx/ui/src/components/ExecutionEnvironmentDetail/ExecutionEnvironmentDetail.test.js deleted file mode 100644 index a0e84e686bad..000000000000 --- a/awx/ui/src/components/ExecutionEnvironmentDetail/ExecutionEnvironmentDetail.test.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import ExecutionEnvironmentDetail from './ExecutionEnvironmentDetail'; - -const mockExecutionEnvironment = { - id: 2, - name: 'Foo', - image: 'quay.io/ansible/awx-ee', - pull: 'missing', - description: '', -}; - -const virtualEnvironment = 'var/lib/awx/custom_env'; - -describe('', () => { - test('should display execution environment detail', async () => { - const wrapper = mountWithContexts( - - ); - const executionEnvironment = wrapper.find('ExecutionEnvironmentDetail'); - expect(executionEnvironment).toHaveLength(1); - expect(executionEnvironment.find('dt').text()).toEqual( - 'Execution Environment' - ); - expect(executionEnvironment.find('dd').text()).toEqual( - mockExecutionEnvironment.name - ); - }); - - test('should display execution environment detail even with a previous virtual env present', async () => { - const wrapper = mountWithContexts( - - ); - const executionEnvironment = wrapper.find('ExecutionEnvironmentDetail'); - expect(executionEnvironment).toHaveLength(1); - expect(executionEnvironment.find('dt').text()).toEqual( - 'Execution Environment' - ); - expect(executionEnvironment.find('dd').text()).toEqual( - mockExecutionEnvironment.name - ); - }); - - test('should display warning missing execution environment', async () => { - const wrapper = mountWithContexts( - - ); - const executionEnvironment = wrapper.find('ExecutionEnvironmentDetail'); - expect(executionEnvironment).toHaveLength(1); - expect(executionEnvironment.find('dt').text()).toEqual( - 'Execution Environment' - ); - expect(executionEnvironment.find('dd').text()).toEqual('Missing resource'); - expect(wrapper.find('ExclamationTrianglePopover').length).toBe(1); - expect(wrapper.find('Popover').length).toBe(1); - }); - - test('should display warning deleted execution environment', async () => { - const wrapper = mountWithContexts( - - ); - const executionEnvironment = wrapper.find('ExecutionEnvironmentDetail'); - expect(executionEnvironment).toHaveLength(1); - expect(executionEnvironment.find('dt').text()).toEqual( - 'Execution Environment' - ); - expect(executionEnvironment.find('dd').text()).toEqual('Missing resource'); - expect(wrapper.find('Tooltip').prop('content')).toEqual( - `Execution environment is missing or deleted.` - ); - }); -}); diff --git a/awx/ui/src/components/ExecutionEnvironmentDetail/index.js b/awx/ui/src/components/ExecutionEnvironmentDetail/index.js deleted file mode 100644 index 7c5efd15b45d..000000000000 --- a/awx/ui/src/components/ExecutionEnvironmentDetail/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ExecutionEnvironmentDetail'; diff --git a/awx/ui/src/components/ExpandCollapse/ExpandCollapse.js b/awx/ui/src/components/ExpandCollapse/ExpandCollapse.js deleted file mode 100644 index 770e29198f96..000000000000 --- a/awx/ui/src/components/ExpandCollapse/ExpandCollapse.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; -import { - Button as PFButton, - ToolbarItem as PFToolbarItem, -} from '@patternfly/react-core'; -import { BarsIcon, EqualsIcon } from '@patternfly/react-icons'; -import styled from 'styled-components'; - -const Button = styled(PFButton)` - padding: 0; - margin: 0; - height: 30px; - width: 30px; - ${(props) => - props.isActive - ? ` - background-color: #007bba; - --pf-c-button--m-plain--active--Color: white; - --pf-c-button--m-plain--focus--Color: white;` - : null}; -`; - -const ToolbarItem = styled(PFToolbarItem)` - & :not(:last-child) { - margin-right: 20px; - } -`; - -// TODO: Recommend renaming this component to avoid confusion -// with ExpandingContainer -function ExpandCollapse({ isCompact, onCompact, onExpand }) { - return ( - <> - - - - - - - - ); -} - -ExpandCollapse.propTypes = { - onCompact: PropTypes.func.isRequired, - onExpand: PropTypes.func.isRequired, - isCompact: PropTypes.bool, -}; - -ExpandCollapse.defaultProps = { - isCompact: true, -}; - -export default ExpandCollapse; diff --git a/awx/ui/src/components/ExpandCollapse/ExpandCollapse.test.js b/awx/ui/src/components/ExpandCollapse/ExpandCollapse.test.js deleted file mode 100644 index eb06b49c519b..000000000000 --- a/awx/ui/src/components/ExpandCollapse/ExpandCollapse.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import ExpandCollapse from './ExpandCollapse'; - -describe('', () => { - const onCompact = jest.fn(); - const onExpand = jest.fn(); - const isCompact = false; - test('initially renders without crashing', () => { - const wrapper = mountWithContexts( - - ); - expect(wrapper.length).toBe(1); - }); -}); diff --git a/awx/ui/src/components/ExpandCollapse/index.js b/awx/ui/src/components/ExpandCollapse/index.js deleted file mode 100644 index 997c5b618120..000000000000 --- a/awx/ui/src/components/ExpandCollapse/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ExpandCollapse'; diff --git a/awx/ui/src/components/FieldWithPrompt/FieldWithPrompt.js b/awx/ui/src/components/FieldWithPrompt/FieldWithPrompt.js deleted file mode 100644 index d58fca986b3c..000000000000 --- a/awx/ui/src/components/FieldWithPrompt/FieldWithPrompt.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import { bool, node, string } from 'prop-types'; - -import { t } from '@lingui/macro'; -import styled from 'styled-components'; -import { CheckboxField } from '../FormField'; -import Popover from '../Popover'; - -const FieldHeader = styled.div` - display: flex; - padding-bottom: var(--pf-c-form__group-label--PaddingBottom); -`; - -const StyledCheckboxField = styled(CheckboxField)` - --pf-c-check__label--FontSize: var(--pf-c-form__label--FontSize); - margin-left: auto; -`; - -function FieldWithPrompt({ - children, - fieldId, - isRequired, - label, - promptId, - promptName, - tooltip, - isDisabled, -}) { - return ( -
- -
- - {tooltip && } -
- -
- {children} -
- ); -} - -FieldWithPrompt.propTypes = { - fieldId: string.isRequired, - isRequired: bool, - label: string.isRequired, - promptId: string.isRequired, - promptName: string.isRequired, - tooltip: node, -}; - -FieldWithPrompt.defaultProps = { - isRequired: false, - tooltip: null, -}; - -export default FieldWithPrompt; diff --git a/awx/ui/src/components/FieldWithPrompt/FieldWithPrompt.test.js b/awx/ui/src/components/FieldWithPrompt/FieldWithPrompt.test.js deleted file mode 100644 index e3361a9576f1..000000000000 --- a/awx/ui/src/components/FieldWithPrompt/FieldWithPrompt.test.js +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { Field, Formik } from 'formik'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import FieldWithPrompt from './FieldWithPrompt'; - -describe('FieldWithPrompt', () => { - let wrapper; - - test('Required asterisk and Popover hidden when not required and tooltip not provided', () => { - wrapper = mountWithContexts( - - {() => ( - - - {() => } - - - )} - - ); - expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(0); - expect(wrapper.find('Popover')).toHaveLength(0); - }); - - test('Required asterisk and Popover shown when required and tooltip provided', () => { - wrapper = mountWithContexts( - - {() => ( - - - {() => } - - - )} - - ); - expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(1); - expect( - wrapper.find('Popover[data-cy="job-template-limit-tooltip"]').length - ).toBe(1); - }); -}); diff --git a/awx/ui/src/components/FieldWithPrompt/index.js b/awx/ui/src/components/FieldWithPrompt/index.js deleted file mode 100644 index 77c1d5bda6c3..000000000000 --- a/awx/ui/src/components/FieldWithPrompt/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './FieldWithPrompt'; diff --git a/awx/ui/src/components/FormActionGroup/FormActionGroup.js b/awx/ui/src/components/FormActionGroup/FormActionGroup.js deleted file mode 100644 index e0d6631ef860..000000000000 --- a/awx/ui/src/components/FormActionGroup/FormActionGroup.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; -import { ActionGroup, Button } from '@patternfly/react-core'; -import { FormFullWidthLayout } from '../FormLayout'; - -const FormActionGroup = ({ onCancel, onSubmit, submitDisabled }) => ( - - - - - - -); - -FormActionGroup.propTypes = { - onCancel: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, - submitDisabled: PropTypes.bool, -}; - -FormActionGroup.defaultProps = { - submitDisabled: false, -}; - -export default FormActionGroup; diff --git a/awx/ui/src/components/FormActionGroup/FormActionGroup.test.js b/awx/ui/src/components/FormActionGroup/FormActionGroup.test.js deleted file mode 100644 index 5068a85226dc..000000000000 --- a/awx/ui/src/components/FormActionGroup/FormActionGroup.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; - -import FormActionGroup from './FormActionGroup'; - -describe('FormActionGroup', () => { - test('should render the expected content', () => { - const wrapper = mountWithContexts( - {}} onCancel={() => {}} /> - ); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/components/FormActionGroup/index.js b/awx/ui/src/components/FormActionGroup/index.js deleted file mode 100644 index 30c9e11f1a6b..000000000000 --- a/awx/ui/src/components/FormActionGroup/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './FormActionGroup'; diff --git a/awx/ui/src/components/FormField/ArrayTextField.js b/awx/ui/src/components/FormField/ArrayTextField.js deleted file mode 100644 index f96505682aad..000000000000 --- a/awx/ui/src/components/FormField/ArrayTextField.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useField } from 'formik'; -import { FormGroup, TextArea } from '@patternfly/react-core'; -import Popover from '../Popover'; - -function ArrayTextField(props) { - const { - id, - helperText, - name, - label, - tooltip, - tooltipMaxWidth, - validate, - isRequired, - type, - ...rest - } = props; - - const [field, meta, helpers] = useField({ name, validate }); - const isValid = !(meta.touched && meta.error); - const value = field.value || []; - - return ( - } - > - - - - ); -} -export default EulaStep; diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/EulaStep.test.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/EulaStep.test.js deleted file mode 100644 index 889b57a7e14b..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/EulaStep.test.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { Formik } from 'formik'; -import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers'; -import EulaStep from './EulaStep'; - -describe('', () => { - let wrapper; - - beforeEach(async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('initially renders without crashing', async () => { - expect(wrapper.find('EulaStep').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionEdit.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionEdit.js deleted file mode 100644 index 1ad850408e92..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionEdit.js +++ /dev/null @@ -1,273 +0,0 @@ -import React, { useCallback, useEffect } from 'react'; -import { useHistory, Link, useRouteMatch } from 'react-router-dom'; -import { t, Trans } from '@lingui/macro'; -import { Formik, useFormikContext } from 'formik'; -import { - Alert, - AlertGroup, - Button, - Form, - Wizard, - WizardContextConsumer, - WizardFooter, -} from '@patternfly/react-core'; -import { ConfigAPI, SettingsAPI, RootAPI } from 'api'; -import useRequest, { useDismissableError } from 'hooks/useRequest'; -import ContentLoading from 'components/ContentLoading'; -import ContentError from 'components/ContentError'; -import { FormSubmitError } from 'components/FormField'; -import { useConfig } from 'contexts/Config'; -import SubscriptionStep from './SubscriptionStep'; -import AnalyticsStep from './AnalyticsStep'; -import EulaStep from './EulaStep'; - -const CustomFooter = ({ isSubmitLoading }) => { - const { values, errors } = useFormikContext(); - const { me, license_info } = useConfig(); - const history = useHistory(); - - return ( - - - {({ activeStep, onNext, onBack }) => ( - <> - {activeStep.id === 'eula-step' ? ( - - ) : ( - - )} - - {license_info?.valid_key && ( - - )} - - )} - - - ); -}; - -function SubscriptionEdit() { - const history = useHistory(); - const { request: updateConfig, license_info } = useConfig(); - const hasValidKey = Boolean(license_info?.valid_key); - const subscriptionMgmtRoute = useRouteMatch({ - path: '/subscription_management', - }); - - const { - isLoading: isContentLoading, - error: contentError, - request: fetchContent, - result: { brandName }, - } = useRequest( - useCallback(async () => { - const { - data: { BRAND_NAME }, - } = await RootAPI.readAssetVariables(); - return { - brandName: BRAND_NAME, - }; - }, []), - { - brandName: null, - } - ); - - useEffect(() => { - if (subscriptionMgmtRoute && hasValidKey) { - history.push('/settings/subscription/edit'); - } - fetchContent(); - }, [fetchContent]); // eslint-disable-line react-hooks/exhaustive-deps - - const { - error: submitError, - isLoading: submitLoading, - result: submitSuccessful, - request: submitRequest, - } = useRequest( - useCallback(async (form) => { - if (form.manifest_file) { - await ConfigAPI.create({ - manifest: form.manifest_file, - }); - } else if (form.subscription) { - await ConfigAPI.attach({ pool_id: form.subscription.pool_id }); - } - - if (!hasValidKey) { - if (form.pendo) { - await SettingsAPI.updateCategory('ui', { - PENDO_TRACKING_STATE: 'detailed', - }); - } else { - await SettingsAPI.updateCategory('ui', { - PENDO_TRACKING_STATE: 'off', - }); - } - - if (form.insights) { - await SettingsAPI.updateCategory('system', { - INSIGHTS_TRACKING_STATE: true, - }); - } else { - await SettingsAPI.updateCategory('system', { - INSIGHTS_TRACKING_STATE: false, - }); - } - } - - await updateConfig(); - - return true; - }, []) // eslint-disable-line react-hooks/exhaustive-deps - ); - - useEffect(() => { - if (submitSuccessful) { - setTimeout(() => { - history.push( - subscriptionMgmtRoute ? '/home' : '/settings/subscription/details' - ); - }, 3000); - } - }, [submitSuccessful, history, subscriptionMgmtRoute]); - - const { error, dismissError } = useDismissableError(submitError); - const handleSubmit = async (values) => { - dismissError(); - await submitRequest(values); - }; - - if (isContentLoading) { - return ; - } - - if (contentError) { - return ; - } - - const steps = [ - { - name: hasValidKey - ? t`Subscription Management` - : `${brandName} ${t`Subscription`}`, - id: 'subscription-step', - component: , - }, - ...(!hasValidKey - ? [ - { - name: t`User and Automation Analytics`, - id: 'analytics-step', - component: , - }, - ] - : []), - { - name: t`End user license agreement`, - component: , - id: 'eula-step', - nextButtonText: t`Submit`, - }, - ]; - - return ( - <> - - {(formik) => ( -
{ - e.preventDefault(); - }} - > - } - height="fit-content" - /> - {error && ( -
- -
- )} - - )} -
- - {submitSuccessful && ( - - {subscriptionMgmtRoute ? ( - - Redirecting to dashboard - - ) : ( - - Redirecting to subscription detail - - )} - - )} - - - ); -} - -export default SubscriptionEdit; diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionEdit.test.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionEdit.test.js deleted file mode 100644 index eba1891c6e38..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionEdit.test.js +++ /dev/null @@ -1,406 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { createMemoryHistory } from 'history'; -import { ConfigAPI, MeAPI, SettingsAPI, RootAPI, UsersAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../../testUtils/enzymeHelpers'; -import SubscriptionEdit from './SubscriptionEdit'; - -jest.mock('../../../../api'); - -const mockConfig = { - me: { - is_superuser: true, - }, - license_info: { - compliant: true, - current_instances: 1, - date_expired: false, - date_warning: true, - free_instances: 1000, - grace_period_remaining: 2904229, - instance_count: 1001, - license_date: '1614401999', - license_type: 'enterprise', - pool_id: '123', - product_name: 'Red Hat Ansible Automation, Standard (5000 Managed Nodes)', - satellite: false, - sku: 'ABC', - subscription_name: - 'Red Hat Ansible Automation, Standard (1001 Managed Nodes)', - support_level: null, - time_remaining: 312229, - trial: false, - valid_key: true, - }, - analytics_status: 'detailed', - version: '1.2.3', -}; - -const emptyConfig = { - me: { - is_superuser: true, - }, - license_info: { - valid_key: false, - }, - request: jest.fn(), -}; - -describe('', () => { - describe('installing a fresh subscription', () => { - let wrapper; - let history; - - beforeAll(async () => { - jest.resetAllMocks(); - RootAPI.readAssetVariables = async () => ({ - data: { - BRAND_NAME: 'Mock', - PENDO_API_KEY: '', - }, - }); - SettingsAPI.readCategory = async () => ({ - data: {}, - }); - history = createMemoryHistory({ - initialEntries: ['/settings/subscription_managment'], - }); - await act(async () => { - wrapper = mountWithContexts(, { - context: { - config: emptyConfig, - router: { history }, - }, - }); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - test('initially renders without crashing', () => { - expect(wrapper.find('SubscriptionEdit').length).toBe(1); - }); - - test('should show all wizard steps when it is a trial or a fresh installation', () => { - expect( - wrapper.find('WizardNavItem[content="Mock Subscription"]').length - ).toBe(1); - expect( - wrapper.find('WizardNavItem[content="User and Automation Analytics"]') - .length - ).toBe(1); - expect( - wrapper.find('WizardNavItem[content="End user license agreement"]') - .length - ).toBe(1); - expect( - wrapper.find('button[aria-label="Cancel subscription edit"]').length - ).toBe(0); - }); - - test('subscription selection type toggle should default to manifest', () => { - expect(wrapper.find('ToggleGroupItem').first().text()).toBe( - 'Subscription manifest' - ); - expect(wrapper.find('ToggleGroupItem').first().props().isSelected).toBe( - true - ); - expect(wrapper.find('ToggleGroupItem').last().text()).toBe( - 'Username / password' - ); - expect(wrapper.find('ToggleGroupItem').last().props().isSelected).toBe( - false - ); - }); - - test('file upload field should upload manifest file', async () => { - expect(wrapper.find('FileUploadField').prop('filename')).toEqual(''); - const mockFile = new Blob(['123'], { type: 'application/zip' }); - mockFile.name = 'mock.zip'; - mockFile.date = new Date(); - await act(async () => { - wrapper.find('FileUpload').invoke('onChange')(mockFile, 'mock.zip'); - }); - await act(async () => { - wrapper.update(); - }); - await act(async () => { - wrapper.update(); - }); - expect(wrapper.find('FileUploadField').prop('filename')).toEqual( - 'mock.zip' - ); - }); - - test('clicking next button should show analytics step', async () => { - wrapper.update(); - await act(async () => { - wrapper.find('button#subscription-wizard-next').simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('AnalyticsStep').length).toBe(1); - expect(wrapper.find('CheckboxField').length).toBe(2); - expect(wrapper.find('FormField').length).toBe(1); - expect(wrapper.find('PasswordField').length).toBe(1); - }); - - test('deselecting insights checkbox should hide username and password fields', async () => { - expect(wrapper.find('input#username-field')).toHaveLength(1); - expect(wrapper.find('input#password-field')).toHaveLength(1); - await act(async () => { - wrapper.find('Checkbox[name="pendo"] input').simulate('change', { - target: { value: false, name: 'pendo' }, - }); - wrapper.find('Checkbox[name="insights"] input').simulate('change', { - target: { value: false, name: 'insights' }, - }); - }); - wrapper.update(); - expect(wrapper.find('input#username-field')).toHaveLength(0); - expect(wrapper.find('input#password-field')).toHaveLength(0); - }); - - test('clicking next button should show eula step', async () => { - await act(async () => { - wrapper.find('button#subscription-wizard-next').simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('EulaStep').length).toBe(1); - expect(wrapper.find('button#subscription-wizard-submit').length).toBe(1); - expect( - wrapper.find('button#subscription-wizard-submit').prop('disabled') - ).toBe(false); - }); - - test('should successfully save on form submission', async () => { - const { window } = global; - global.window.pendo = { initialize: async () => ({}) }; - ConfigAPI.read = async () => ({ - data: mockConfig, - }); - MeAPI.read = async () => ({ - data: { - results: [ - { - is_superuser: true, - }, - ], - }, - }); - ConfigAPI.attach = async () => ({}); - ConfigAPI.create = async () => ({ - data: mockConfig, - }); - UsersAPI.readAdminOfOrganizations({ - data: {}, - }); - expect(wrapper.find('Alert[title="Save successful"]')).toHaveLength(0); - await act(async () => - wrapper.find('button[aria-label="Submit"]').simulate('click') - ); - wrapper.update(); - waitForElement(wrapper, 'Alert[title="Save successful"]'); - global.window = window; - }); - }); - - describe('editing with a valid subscription', () => { - let wrapper; - let history; - - beforeAll(async () => { - SettingsAPI.readCategory = async () => ({ - data: { - SUBSCRIPTIONS_PASSWORD: 'mock_password', - SUBSCRIPTIONS_USERNAME: 'mock_username', - INSIGHTS_TRACKING_STATE: false, - PENDO: 'off', - }, - }); - ConfigAPI.readSubscriptions = async () => ({ - data: [ - { - subscription_name: 'mock subscription 50 instances', - instance_count: 50, - license_date: new Date(), - pool_id: 999, - }, - ], - }); - history = createMemoryHistory({ - initialEntries: ['/settings/subscription/edit'], - }); - await act(async () => { - wrapper = mountWithContexts(, { - context: { - config: { - mockConfig, - request: jest.fn(), - }, - me: { - is_superuser: true, - }, - router: { history }, - }, - }); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - test('should hide analytics step when editing a current subscription', async () => { - expect( - wrapper.find('WizardNavItem[content="Subscription Management"]').length - ).toBe(1); - expect( - wrapper.find('WizardNavItem[content="User and Automation Analytics"]') - .length - ).toBe(0); - expect( - wrapper.find('WizardNavItem[content="End user license agreement"]') - .length - ).toBe(1); - }); - - test('Username/password toggle button should show username credential fields', async () => { - expect(wrapper.find('ToggleGroupItem').last().props().isSelected).toBe( - false - ); - wrapper - .find('ToggleGroupItem[text="Username / password"] button') - .simulate('click'); - wrapper.update(); - expect(wrapper.find('ToggleGroupItem').last().props().isSelected).toBe( - true - ); - expect(wrapper.find('input#username-field').prop('value')).toEqual(''); - expect(wrapper.find('input#password-field').prop('value')).toEqual(''); - await act(async () => { - wrapper.find('input#username-field').simulate('change', { - target: { value: 'username-cred', name: 'username' }, - }); - wrapper.find('input#password-field').simulate('change', { - target: { value: 'password-cred', name: 'password' }, - }); - }); - wrapper.update(); - expect(wrapper.find('input#username-field').prop('value')).toEqual( - 'username-cred' - ); - expect(wrapper.find('input#password-field').prop('value')).toEqual( - 'password-cred' - ); - }); - - test('should open subscription selection modal', async () => { - expect(wrapper.find('Flex[id="selected-subscription-file"]').length).toBe( - 0 - ); - await act(async () => { - wrapper - .find('SubscriptionStep button[aria-label="Get subscriptions"]') - .simulate('click'); - }); - wrapper.update(); - await waitForElement(wrapper, 'SubscriptionModal'); - await act(async () => { - wrapper - .find('SubscriptionModal SelectColumn') - .first() - .invoke('onSelect')(); - }); - wrapper.update(); - await act(async () => - wrapper.find('Button[aria-label="Confirm selection"]').prop('onClick')() - ); - wrapper.update(); - await waitForElement( - wrapper, - 'SubscriptionModal', - (el) => el.length === 0 - ); - }); - - test('should show selected subscription name', () => { - expect(wrapper.find('Flex[id="selected-subscription"]').length).toBe(1); - expect(wrapper.find('Flex[id="selected-subscription"] i').text()).toBe( - 'mock subscription 50 instances' - ); - }); - test('next should skip analytics step and navigate to eula step', async () => { - await act(async () => { - wrapper.find('button#subscription-wizard-next').simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('SubscriptionStep').length).toBe(0); - expect(wrapper.find('AnalyticsStep').length).toBe(0); - expect(wrapper.find('EulaStep').length).toBe(1); - expect( - wrapper.find('button#subscription-wizard-submit').prop('disabled') - ).toBe(false); - }); - - test('should successfully send request to api on form submission', async () => { - expect(wrapper.find('EulaStep').length).toBe(1); - ConfigAPI.read = async () => ({ - data: { - mockConfig, - }, - }); - MeAPI.read = async () => ({ - data: { - results: [ - { - is_superuser: true, - }, - ], - }, - }); - ConfigAPI.attach = async () => ({}); - ConfigAPI.create = async () => ({}); - UsersAPI.readAdminOfOrganizations = async () => ({ - data: {}, - }); - waitForElement( - wrapper, - 'Alert[title="Save successful"]', - (el) => el.length === 0 - ); - await act(async () => - wrapper.find('button#subscription-wizard-submit').prop('onClick')() - ); - wrapper.update(); - waitForElement(wrapper, 'Alert[title="Save successful"]'); - }); - - test('should navigate to subscription details on cancel', async () => { - expect( - wrapper.find('button[aria-label="Cancel subscription edit"]').length - ).toBe(1); - await act(async () => { - wrapper - .find('button[aria-label="Cancel subscription edit"]') - .invoke('onClick')(); - }); - expect(history.location.pathname).toEqual( - '/settings/subscription/details' - ); - }); - }); - - test('should throw a content error', async () => { - RootAPI.readAssetVariables = jest.fn(); - RootAPI.readAssetVariables.mockRejectedValueOnce(new Error()); - let wrapper; - await act(async () => { - wrapper = mountWithContexts(, { - context: { - config: emptyConfig, - }, - }); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - await waitForElement(wrapper, 'ContentError', (el) => el.length === 1); - }); -}); diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.js deleted file mode 100644 index 99a9c993af9f..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.js +++ /dev/null @@ -1,187 +0,0 @@ -import React, { useCallback, useEffect } from 'react'; - -import { t, Trans } from '@lingui/macro'; -import { - Button, - EmptyState, - EmptyStateIcon, - EmptyStateBody, - Modal, - Title, -} from '@patternfly/react-core'; -import { - TableComposable, - Tbody, - Td, - Th, - Thead, - Tr, -} from '@patternfly/react-table'; -import { ExclamationTriangleIcon } from '@patternfly/react-icons'; - -import { ConfigAPI } from 'api'; -import { formatDateString } from 'util/dates'; -import useRequest from 'hooks/useRequest'; -import useSelected from 'hooks/useSelected'; -import ErrorDetail from 'components/ErrorDetail'; -import ContentEmpty from 'components/ContentEmpty'; -import ContentLoading from 'components/ContentLoading'; - -function SubscriptionModal({ - subscriptionCreds = {}, - selectedSubscription = null, - onClose, - onConfirm, -}) { - const { - isLoading, - error, - request: fetchSubscriptions, - result: subscriptions, - } = useRequest( - useCallback(async () => { - if (!subscriptionCreds.username || !subscriptionCreds.password) { - return []; - } - const { data } = await ConfigAPI.readSubscriptions( - subscriptionCreds.username, - subscriptionCreds.password - ); - - // Ensure unique ids for each subscription - // because it is possible to have multiple - // subscriptions with the same pool_id - let repeatId = 1; - data.forEach((i) => { - i.id = repeatId++; - }); - - return data; - }, []), // eslint-disable-line react-hooks/exhaustive-deps - [] - ); - - const { selected, setSelected } = useSelected(subscriptions); - - const handleConfirm = () => { - const [subscription] = selected; - onConfirm(subscription); - onClose(); - }; - - useEffect(() => { - fetchSubscriptions(); - }, [fetchSubscriptions]); - - useEffect(() => { - if (selectedSubscription?.id) { - setSelected([selectedSubscription]); - } - }, []); // eslint-disable-line react-hooks/exhaustive-deps - - return ( - - Select - , - , - ]} - > - {isLoading && } - {!isLoading && error && ( - - - - <Trans>No subscriptions found</Trans> - - - - We were unable to locate licenses associated with this account. - {' '} - - - - - )} - {!isLoading && !error && subscriptions?.length === 0 && ( - - )} - {!isLoading && !error && subscriptions?.length > 0 && ( - - - - - {t`Name`} - {t`Managed nodes`} - {t`Expires`} - - - - {subscriptions.map((subscription) => ( - - setSelected([subscription]), - isSelected: selected.some( - (row) => row.id === subscription.id - ), - variant: 'radio', - rowIndex: `row-${subscription.id}`, - }} - /> - {subscription.subscription_name} - - {subscription.instance_count} - - - {formatDateString( - new Date(subscription.license_date * 1000).toISOString(), - 'UTC' - )} - - - ))} - - - )} - - ); -} - -export default SubscriptionModal; diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.test.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.test.js deleted file mode 100644 index 4c8fa843f225..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionModal.test.js +++ /dev/null @@ -1,155 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { ConfigAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../../testUtils/enzymeHelpers'; -import SubscriptionModal from './SubscriptionModal'; - -jest.mock('../../../../api'); - -describe('', () => { - let wrapper; - const onConfirm = jest.fn(); - const onClose = jest.fn(); - - beforeAll(async () => { - ConfigAPI.readSubscriptions = async () => ({ - data: [ - { - subscription_name: 'mock A', - instance_count: 100, - license_date: 1714000271, - pool_id: 7, - }, - { - subscription_name: 'mock B', - instance_count: 200, - license_date: 1714000271, - pool_id: 8, - }, - { - subscription_name: 'mock C', - instance_count: 30, - license_date: 1714000271, - pool_id: 9, - }, - ], - }); - await act(async () => { - wrapper = mountWithContexts( - - ); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - test('initially renders without crashing', async () => { - expect(wrapper.find('SubscriptionModal').length).toBe(1); - }); - - test('should render header', async () => { - wrapper.update(); - const header = wrapper.find('tr').first().find('th'); - expect(header.at(0).text()).toEqual(''); - expect(header.at(1).text()).toEqual('Name'); - expect(header.at(2).text()).toEqual('Managed nodes'); - expect(header.at(3).text()).toEqual('Expires'); - }); - - test('should render subscription rows', async () => { - const rows = wrapper.find('tbody tr'); - expect(rows).toHaveLength(3); - const firstRow = rows.at(0).find('td'); - expect(firstRow.at(0).find('input[type="radio"]')).toHaveLength(1); - expect(firstRow.at(1).text()).toEqual('mock A'); - expect(firstRow.at(2).text()).toEqual('100'); - expect(firstRow.at(3).text()).toEqual('4/24/2024, 11:11:11 PM'); - }); - - test('submit button should call onConfirm', async () => { - expect( - wrapper.find('Button[aria-label="Confirm selection"]').prop('isDisabled') - ).toBe(true); - await act(async () => { - wrapper - .find('SubscriptionModal SelectColumn') - .first() - .invoke('onSelect')(); - }); - wrapper.update(); - expect( - wrapper.find('Button[aria-label="Confirm selection"]').prop('isDisabled') - ).toBe(false); - expect(onConfirm).toHaveBeenCalledTimes(0); - expect(onClose).toHaveBeenCalledTimes(0); - await act(async () => - wrapper.find('Button[aria-label="Confirm selection"]').prop('onClick')() - ); - expect(onConfirm).toHaveBeenCalledTimes(1); - expect(onClose).toHaveBeenCalledTimes(1); - }); - - test('should show empty content', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - await waitForElement(wrapper, 'ContentEmpty', (el) => el.length === 1); - }); - }); - - test('should auto-select current selected subscription', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - await waitForElement(wrapper, 'table'); - expect(wrapper.find('tr[id="row-1"] input').prop('checked')).toBe(false); - expect(wrapper.find('tr[id="row-2"] input').prop('checked')).toBe(true); - expect(wrapper.find('tr[id="row-3"] input').prop('checked')).toBe(false); - }); - }); - - test('should display error detail message', async () => { - ConfigAPI.readSubscriptions = jest.fn(); - ConfigAPI.readSubscriptions.mockRejectedValueOnce(new Error()); - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - await waitForElement(wrapper, 'ErrorDetail', (el) => el.length === 1); - }); -}); diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionStep.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionStep.js deleted file mode 100644 index 333a7939fdac..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionStep.js +++ /dev/null @@ -1,266 +0,0 @@ -import React, { useState } from 'react'; - -import { Trans, t } from '@lingui/macro'; -import { useField, useFormikContext } from 'formik'; -import styled from 'styled-components'; -import { TimesIcon } from '@patternfly/react-icons'; -import { - Button, - Divider, - FileUpload, - Flex, - FlexItem, - FormGroup, - ToggleGroup, - ToggleGroupItem, - Tooltip, -} from '@patternfly/react-core'; -import { useConfig } from 'contexts/Config'; -import getDocsBaseUrl from 'util/getDocsBaseUrl'; -import useModal from 'hooks/useModal'; -import FormField, { PasswordField } from 'components/FormField'; -import Popover from 'components/Popover'; -import SubscriptionModal from './SubscriptionModal'; - -const LICENSELINK = 'https://www.ansible.com/license'; -const FileUploadField = styled(FormGroup)` - && { - max-width: 500px; - width: 100%; - } -`; - -function SubscriptionStep() { - const config = useConfig(); - const hasValidKey = Boolean(config?.license_info?.valid_key); - - const { values } = useFormikContext(); - - const [isSelected, setIsSelected] = useState( - values.subscription ? 'selectSubscription' : 'uploadManifest' - ); - const { isModalOpen, toggleModal, closeModal } = useModal(); - const [manifest, manifestMeta, manifestHelpers] = useField('manifest_file'); - const [manifestFilename, , manifestFilenameHelpers] = - useField('manifest_filename'); - const [subscription, , subscriptionHelpers] = useField('subscription'); - const [username, usernameMeta, usernameHelpers] = useField('username'); - const [password, passwordMeta, passwordHelpers] = useField('password'); - - return ( - - {!hasValidKey && ( - <> - - {t`Welcome to Red Hat Ansible Automation Platform! - Please complete the steps below to activate your subscription.`} - -

- {t`If you do not have a subscription, you can visit - Red Hat to obtain a trial subscription.`} -

- - - - )} -

{t`Select your Ansible Automation Platform subscription to use.`}

- - setIsSelected('uploadManifest')} - id="subscription-manifest" - /> - setIsSelected('selectSubscription')} - id="username-password" - /> - - {isSelected === 'uploadManifest' ? ( - <> -

- - Upload a Red Hat Subscription Manifest containing your - subscription. To generate your subscription manifest, go to{' '} - {' '} - on the Red Hat Customer Portal. - -

- - A subscription manifest is an export of a Red Hat - Subscription. To generate a subscription manifest, go to{' '} - - . For more information, see the{' '} - - . - - } - /> - } - > - manifestHelpers.setError(true), - }} - onChange={(value, filename) => { - if (!value) { - manifestHelpers.setValue(null); - manifestFilenameHelpers.setValue(''); - usernameHelpers.setValue(usernameMeta.initialValue); - passwordHelpers.setValue(passwordMeta.initialValue); - return; - } - - try { - const raw = new FileReader(); - raw.readAsBinaryString(value); - raw.onload = () => { - const rawValue = btoa(raw.result); - manifestHelpers.setValue(rawValue); - manifestFilenameHelpers.setValue(filename); - }; - } catch (err) { - manifestHelpers.setError(err); - } - }} - /> - - - ) : ( - <> -

- {t`Provide your Red Hat or Red Hat Satellite credentials - below and you can choose from a list of your available subscriptions. - The credentials you use will be stored for future use in - retrieving renewal or expanded subscriptions.`} -

- - - - - {isModalOpen && ( - subscriptionHelpers.setValue(value)} - /> - )} - - {subscription.value && ( - - {t`Selected`} - - {subscription?.value?.subscription_name} - - - - - - )} - - )} -
- ); -} -export default SubscriptionStep; diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionStep.test.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionStep.test.js deleted file mode 100644 index e539a06e08f5..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/SubscriptionStep.test.js +++ /dev/null @@ -1,120 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { Formik } from 'formik'; -import { mountWithContexts } from '../../../../../testUtils/enzymeHelpers'; -import SubscriptionStep from './SubscriptionStep'; - -describe('', () => { - let wrapper; - - beforeAll(async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - test('initially renders without crashing', async () => { - expect(wrapper.find('SubscriptionStep').length).toBe(1); - }); - - test('should update filename when a manifest zip file is uploaded', async () => { - expect(wrapper.find('FileUploadField')).toHaveLength(1); - expect(wrapper.find('label').text()).toEqual( - 'Red Hat subscription manifest' - ); - expect(wrapper.find('FileUploadField').prop('value')).toEqual(null); - expect(wrapper.find('FileUploadField').prop('filename')).toEqual(''); - const mockFile = new Blob(['123'], { type: 'application/zip' }); - mockFile.name = 'new file name'; - mockFile.date = new Date(); - await act(async () => { - wrapper.find('FileUpload').invoke('onChange')(mockFile, 'new file name'); - }); - await act(async () => { - wrapper.update(); - }); - await act(async () => { - wrapper.update(); - }); - expect(wrapper.find('FileUploadField').prop('value')).toEqual( - expect.stringMatching(/^[\x00-\x7F]+$/) // eslint-disable-line no-control-regex - ); - expect(wrapper.find('FileUploadField').prop('filename')).toEqual( - 'new file name' - ); - }); - - test('clear button should clear manifest value and filename', async () => { - await act(async () => { - wrapper - .find('FileUpload .pf-c-input-group button') - .last() - .simulate('click'); - }); - wrapper.update(); - expect(wrapper.find('FileUploadField').prop('value')).toEqual(null); - expect(wrapper.find('FileUploadField').prop('filename')).toEqual(''); - }); - - test('FileUpload should throw an error', async () => { - expect( - wrapper.find('div#subscription-manifest-helper.pf-m-error') - ).toHaveLength(0); - await act(async () => { - wrapper.find('FileUpload').invoke('onChange')('✓', 'new file name'); - }); - wrapper.update(); - expect( - wrapper.find('div#subscription-manifest-helper.pf-m-error') - ).toHaveLength(1); - expect(wrapper.find('div#subscription-manifest-helper').text()).toContain( - 'Invalid file format. Please upload a valid Red Hat Subscription Manifest.' - ); - }); - - test('Username/password toggle button should show username credential fields', async () => { - expect(wrapper.find('ToggleGroupItem').last().props().isSelected).toBe( - false - ); - wrapper - .find('ToggleGroupItem[text="Username / password"] button') - .simulate('click'); - wrapper.update(); - expect(wrapper.find('ToggleGroupItem').last().props().isSelected).toBe( - true - ); - await act(async () => { - wrapper.find('input#username-field').simulate('change', { - target: { value: 'username-cred', name: 'username' }, - }); - wrapper.find('input#password-field').simulate('change', { - target: { value: 'password-cred', name: 'password' }, - }); - }); - wrapper.update(); - expect(wrapper.find('input#username-field').prop('value')).toEqual( - 'username-cred' - ); - expect(wrapper.find('input#password-field').prop('value')).toEqual( - 'password-cred' - ); - }); -}); diff --git a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/index.js b/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/index.js deleted file mode 100644 index 1b9aeadaec11..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/SubscriptionEdit/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './SubscriptionEdit'; diff --git a/awx/ui/src/screens/Setting/Subscription/index.js b/awx/ui/src/screens/Setting/Subscription/index.js deleted file mode 100644 index 41a92af34fa3..000000000000 --- a/awx/ui/src/screens/Setting/Subscription/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Subscription'; diff --git a/awx/ui/src/screens/Setting/TACACS/TACACS.js b/awx/ui/src/screens/Setting/TACACS/TACACS.js deleted file mode 100644 index 720ac4d1ca7b..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACS.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { Link, Redirect, Route, Switch } from 'react-router-dom'; - -import { t } from '@lingui/macro'; -import { PageSection, Card } from '@patternfly/react-core'; -import ContentError from 'components/ContentError'; -import TACACSDetail from './TACACSDetail'; -import TACACSEdit from './TACACSEdit'; - -function TACACS() { - const baseURL = '/settings/tacacs'; - return ( - - - - - - - - - - - - - {t`View TACACS+ settings`} - - - - - - ); -} - -export default TACACS; diff --git a/awx/ui/src/screens/Setting/TACACS/TACACS.test.js b/awx/ui/src/screens/Setting/TACACS/TACACS.test.js deleted file mode 100644 index 4a44ac73a794..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACS.test.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { createMemoryHistory } from 'history'; -import { SettingsProvider } from 'contexts/Settings'; -import { SettingsAPI } from 'api'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; -import mockAllOptions from '../shared/data.allSettingOptions.json'; -import TACACS from './TACACS'; - -jest.mock('../../../api/models/Settings'); -SettingsAPI.readCategory.mockResolvedValue({ - data: { - TACACSPLUS_HOST: 'mockhost', - TACACSPLUS_PORT: 49, - TACACSPLUS_SECRET: '$encrypted$', - TACACSPLUS_SESSION_TIMEOUT: 5, - TACACSPLUS_AUTH_PROTOCOL: 'ascii', - }, -}); - -describe('', () => { - let wrapper; - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should render TACACS+ details', async () => { - const history = createMemoryHistory({ - initialEntries: ['/settings/tacacs/details'], - }); - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { router: { history } }, - } - ); - }); - expect(wrapper.find('TACACSDetail').length).toBe(1); - }); - - test('should render TACACS+ edit', async () => { - const history = createMemoryHistory({ - initialEntries: ['/settings/tacacs/edit'], - }); - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { router: { history } }, - } - ); - }); - expect(wrapper.find('TACACSEdit').length).toBe(1); - }); - - test('should show content error when user navigates to erroneous route', async () => { - const history = createMemoryHistory({ - initialEntries: ['/settings/tacacs/foo'], - }); - await act(async () => { - wrapper = mountWithContexts(, { - context: { router: { history } }, - }); - }); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.js b/awx/ui/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.js deleted file mode 100644 index 2311759d5a11..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.js +++ /dev/null @@ -1,117 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { Link } from 'react-router-dom'; - -import { t } from '@lingui/macro'; -import { Button, Alert as PFAlert } from '@patternfly/react-core'; -import { CaretLeftIcon } from '@patternfly/react-icons'; -import styled from 'styled-components'; -import { CardBody, CardActionsRow } from 'components/Card'; -import ContentLoading from 'components/ContentLoading'; -import ContentError from 'components/ContentError'; -import RoutedTabs from 'components/RoutedTabs'; -import { SettingsAPI } from 'api'; -import useRequest from 'hooks/useRequest'; -import { DetailList } from 'components/DetailList'; -import { useConfig } from 'contexts/Config'; -import { useSettings } from 'contexts/Settings'; -import { SettingDetail } from '../../shared'; - -const Alert = styled(PFAlert)` - margin-bottom: 20px; -`; - -function TACACSDetail() { - const { me } = useConfig(); - const { GET: options } = useSettings(); - - const { - isLoading, - error, - request, - result: tacacs, - } = useRequest( - useCallback(async () => { - const { data } = await SettingsAPI.readCategory('tacacsplus'); - return data; - }, []), - null - ); - - useEffect(() => { - request(); - }, [request]); - - const tabsArray = [ - { - name: ( - <> - - {t`Back to Settings`} - - ), - link: `/settings`, - id: 99, - }, - { - name: t`Details`, - link: `/settings/tacacs/details`, - id: 0, - }, - ]; - if (isLoading) { - return ; - } - if (!isLoading && error) { - return ; - } - return ( - <> - - - {isLoading && } - {!isLoading && error && } - {!isLoading && tacacs && ( - <> - - - {Object.keys(tacacs).map((key) => { - const record = options?.[key]; - return ( - - ); - })} - - - )} - {me?.is_superuser && ( - - - - )} - - - ); -} - -export default TACACSDetail; diff --git a/awx/ui/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.js b/awx/ui/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.js deleted file mode 100644 index 3244c1110d26..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACSDetail/TACACSDetail.test.js +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { SettingsProvider } from 'contexts/Settings'; -import { SettingsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../../testUtils/enzymeHelpers'; -import { assertDetail } from '../../shared/settingTestUtils'; -import mockAllOptions from '../../shared/data.allSettingOptions.json'; -import TACACSDetail from './TACACSDetail'; - -jest.mock('../../../../api'); - -describe('', () => { - let wrapper; - - beforeEach(() => { - SettingsAPI.readCategory.mockResolvedValue({ - data: { - TACACSPLUS_HOST: 'mockhost', - TACACSPLUS_PORT: 49, - TACACSPLUS_SECRET: '$encrypted$', - TACACSPLUS_SESSION_TIMEOUT: 5, - TACACSPLUS_AUTH_PROTOCOL: 'ascii', - }, - }); - }); - - beforeEach(async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - test('initially renders without crashing', () => { - expect(wrapper.find('TACACSDetail').length).toBe(1); - }); - - test('should render expected tabs', () => { - const expectedTabs = ['Back to Settings', 'Details']; - wrapper.find('RoutedTabs li').forEach((tab, index) => { - expect(tab.text()).toEqual(expectedTabs[index]); - }); - }); - - test('should render expected details', () => { - expect(wrapper.find('Alert').prop('title')).toBe( - 'This feature is deprecated and will be removed in a future release.' - ); - assertDetail(wrapper, 'TACACS+ Server', 'mockhost'); - assertDetail(wrapper, 'TACACS+ Port', '49'); - assertDetail(wrapper, 'TACACS+ Secret', 'Encrypted'); - assertDetail(wrapper, 'TACACS+ Auth Session Timeout', '5 seconds'); - assertDetail(wrapper, 'TACACS+ Authentication Protocol', 'ascii'); - }); - - test('should hide edit button from non-superusers', async () => { - const config = { - me: { - is_superuser: false, - }, - }; - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { config }, - } - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('Button[aria-label="Edit"]').exists()).toBeFalsy(); - }); - - test('should display content error when api throws error on initial render', async () => { - SettingsAPI.readCategory.mockRejectedValue(new Error()); - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/TACACS/TACACSDetail/index.js b/awx/ui/src/screens/Setting/TACACS/TACACSDetail/index.js deleted file mode 100644 index 1720e8b92180..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACSDetail/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './TACACSDetail'; diff --git a/awx/ui/src/screens/Setting/TACACS/TACACSEdit/TACACSEdit.js b/awx/ui/src/screens/Setting/TACACS/TACACSEdit/TACACSEdit.js deleted file mode 100644 index d1894b53c807..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACSEdit/TACACSEdit.js +++ /dev/null @@ -1,141 +0,0 @@ -import React, { useCallback, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Formik } from 'formik'; -import { Form } from '@patternfly/react-core'; -import { CardBody } from 'components/Card'; -import ContentError from 'components/ContentError'; -import ContentLoading from 'components/ContentLoading'; -import { FormSubmitError } from 'components/FormField'; -import { FormColumnLayout } from 'components/FormLayout'; -import { useSettings } from 'contexts/Settings'; -import useModal from 'hooks/useModal'; -import useRequest from 'hooks/useRequest'; -import { SettingsAPI } from 'api'; -import { - ChoiceField, - EncryptedField, - InputField, -} from '../../shared/SharedFields'; -import { RevertAllAlert, RevertFormActionGroup } from '../../shared'; - -function TACACSEdit() { - const history = useHistory(); - const { isModalOpen, toggleModal, closeModal } = useModal(); - const { PUT: options } = useSettings(); - - const { - isLoading, - error, - request: fetchTACACS, - result: tacacs, - } = useRequest( - useCallback(async () => { - const { data } = await SettingsAPI.readCategory('tacacsplus'); - const mergedData = {}; - Object.keys(data).forEach((key) => { - mergedData[key] = options[key]; - mergedData[key].value = data[key]; - }); - return mergedData; - }, [options]), - null - ); - - useEffect(() => { - fetchTACACS(); - }, [fetchTACACS]); - - const { error: submitError, request: submitForm } = useRequest( - useCallback( - async (values) => { - await SettingsAPI.updateAll(values); - history.push('/settings/tacacs/details'); - }, - [history] - ), - null - ); - - const { error: revertError, request: revertAll } = useRequest( - useCallback(async () => { - await SettingsAPI.revertCategory('tacacsplus'); - }, []), - null - ); - - const handleSubmit = async (form) => { - await submitForm(form); - }; - - const handleRevertAll = async () => { - await revertAll(); - - closeModal(); - - history.push('/settings/tacacs/details'); - }; - - const handleCancel = () => { - history.push('/settings/tacacs/details'); - }; - - const initialValues = (fields) => - Object.keys(fields).reduce((acc, key) => { - acc[key] = fields[key].value ?? ''; - return acc; - }, {}); - - return ( - - {isLoading && } - {!isLoading && error && } - {!isLoading && tacacs && ( - - {(formik) => ( -
- - - - - - - {submitError && } - {revertError && } - - - {isModalOpen && ( - - )} - - )} -
- )} -
- ); -} - -export default TACACSEdit; diff --git a/awx/ui/src/screens/Setting/TACACS/TACACSEdit/TACACSEdit.test.js b/awx/ui/src/screens/Setting/TACACS/TACACSEdit/TACACSEdit.test.js deleted file mode 100644 index 4e41ddd58b5f..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACSEdit/TACACSEdit.test.js +++ /dev/null @@ -1,163 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { createMemoryHistory } from 'history'; -import { SettingsProvider } from 'contexts/Settings'; -import { SettingsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../../testUtils/enzymeHelpers'; -import mockAllOptions from '../../shared/data.allSettingOptions.json'; -import TACACSEdit from './TACACSEdit'; - -jest.mock('../../../../api/'); - -describe('', () => { - let wrapper; - let history; - - beforeEach(() => { - SettingsAPI.revertCategory.mockResolvedValue({}); - SettingsAPI.updateAll.mockResolvedValue({}); - SettingsAPI.readCategory.mockResolvedValue({ - data: { - TACACSPLUS_HOST: 'mockhost', - TACACSPLUS_PORT: 49, - TACACSPLUS_SECRET: '$encrypted$', - TACACSPLUS_SESSION_TIMEOUT: 123, - TACACSPLUS_AUTH_PROTOCOL: 'ascii', - }, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - beforeEach(async () => { - history = createMemoryHistory({ - initialEntries: ['/settings/tacacs/edit'], - }); - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { router: { history } }, - } - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - test('initially renders without crashing', () => { - expect(wrapper.find('TACACSEdit').length).toBe(1); - }); - - test('should display expected form fields', async () => { - expect(wrapper.find('FormGroup[label="TACACS+ Server"]').length).toBe(1); - expect(wrapper.find('FormGroup[label="TACACS+ Port"]').length).toBe(1); - expect(wrapper.find('FormGroup[label="TACACS+ Secret"]').length).toBe(1); - expect( - wrapper.find('FormGroup[label="TACACS+ Auth Session Timeout"]').length - ).toBe(1); - expect( - wrapper.find('FormGroup[label="TACACS+ Authentication Protocol"]').length - ).toBe(1); - }); - - test('should successfully send default values to api on form revert all', async () => { - expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0); - expect(wrapper.find('RevertAllAlert')).toHaveLength(0); - await act(async () => { - wrapper - .find('button[aria-label="Revert all to default"]') - .invoke('onClick')(); - }); - wrapper.update(); - expect(wrapper.find('RevertAllAlert')).toHaveLength(1); - await act(async () => { - wrapper - .find('RevertAllAlert button[aria-label="Confirm revert all"]') - .invoke('onClick')(); - }); - wrapper.update(); - expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1); - expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('tacacsplus'); - }); - - test('should successfully send request to api on form submission', async () => { - act(() => { - wrapper.find('input#TACACSPLUS_HOST').simulate('change', { - target: { value: 'new_host', name: 'TACACSPLUS_HOST' }, - }); - wrapper.find('input#TACACSPLUS_PORT').simulate('change', { - target: { value: 999, name: 'TACACSPLUS_PORT' }, - }); - wrapper - .find( - 'FormGroup[fieldId="TACACSPLUS_SECRET"] button[aria-label="Revert"]' - ) - .invoke('onClick')(); - }); - wrapper.update(); - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1); - expect(SettingsAPI.updateAll).toHaveBeenCalledWith({ - TACACSPLUS_HOST: 'new_host', - TACACSPLUS_PORT: 999, - TACACSPLUS_SECRET: '', - TACACSPLUS_SESSION_TIMEOUT: 123, - TACACSPLUS_AUTH_PROTOCOL: 'ascii', - }); - }); - - test('should navigate to tacacs detail on successful submission', async () => { - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - expect(history.location.pathname).toEqual('/settings/tacacs/details'); - }); - - test('should navigate to tacacs detail when cancel is clicked', async () => { - await act(async () => { - wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); - }); - expect(history.location.pathname).toEqual('/settings/tacacs/details'); - }); - - test('should display error message on unsuccessful submission', async () => { - const error = { - response: { - data: { detail: 'An error occurred' }, - }, - }; - SettingsAPI.updateAll.mockImplementation(() => Promise.reject(error)); - expect(wrapper.find('FormSubmitError').length).toBe(0); - expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0); - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - wrapper.update(); - expect(wrapper.find('FormSubmitError').length).toBe(1); - expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1); - }); - - test('should display ContentError on throw', async () => { - SettingsAPI.readCategory.mockImplementationOnce(() => - Promise.reject(new Error()) - ); - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/TACACS/TACACSEdit/index.js b/awx/ui/src/screens/Setting/TACACS/TACACSEdit/index.js deleted file mode 100644 index 2b95f71aa8b6..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/TACACSEdit/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './TACACSEdit'; diff --git a/awx/ui/src/screens/Setting/TACACS/index.js b/awx/ui/src/screens/Setting/TACACS/index.js deleted file mode 100644 index d1cb31279ef7..000000000000 --- a/awx/ui/src/screens/Setting/TACACS/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './TACACS'; diff --git a/awx/ui/src/screens/Setting/UI/UI.js b/awx/ui/src/screens/Setting/UI/UI.js deleted file mode 100644 index a2e683e15d22..000000000000 --- a/awx/ui/src/screens/Setting/UI/UI.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { Link, Redirect, Route, Switch } from 'react-router-dom'; - -import { t } from '@lingui/macro'; -import { PageSection, Card } from '@patternfly/react-core'; -import ContentError from 'components/ContentError'; -import UIDetail from './UIDetail'; -import UIEdit from './UIEdit'; - -function UI() { - const baseURL = '/settings/ui'; - return ( - - - - - - - - - - - - - - {t`View User Interface settings`} - - - - - - - ); -} - -export default UI; diff --git a/awx/ui/src/screens/Setting/UI/UI.test.js b/awx/ui/src/screens/Setting/UI/UI.test.js deleted file mode 100644 index 84e1d63a54ef..000000000000 --- a/awx/ui/src/screens/Setting/UI/UI.test.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { createMemoryHistory } from 'history'; -import { SettingsAPI } from 'api'; -import { SettingsProvider } from 'contexts/Settings'; -import { - mountWithContexts, - waitForElement, -} from '../../../../testUtils/enzymeHelpers'; -import mockAllOptions from '../shared/data.allSettingOptions.json'; -import UI from './UI'; - -jest.mock('../../../api/models/Settings'); -SettingsAPI.readCategory.mockResolvedValue({ - data: { - CUSTOM_LOGIN_INFO: '', - CUSTOM_LOGO: '', - PENDO_TRACKING_STATE: 'off', - }, -}); - -describe('', () => { - let wrapper; - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should render user interface details', async () => { - const history = createMemoryHistory({ - initialEntries: ['/settings/ui/details'], - }); - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { router: { history } }, - } - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('UIDetail').length).toBe(1); - }); - - test('should render user interface edit', async () => { - const history = createMemoryHistory({ - initialEntries: ['/settings/ui/edit'], - }); - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { router: { history } }, - } - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('UIEdit').length).toBe(1); - }); - - test('should show content error when user navigates to erroneous route', async () => { - const history = createMemoryHistory({ - initialEntries: ['/settings/ui/foo'], - }); - await act(async () => { - wrapper = mountWithContexts(, { - context: { router: { history } }, - }); - }); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/UI/UIDetail/UIDetail.js b/awx/ui/src/screens/Setting/UI/UIDetail/UIDetail.js deleted file mode 100644 index f02f78d4f743..000000000000 --- a/awx/ui/src/screens/Setting/UI/UIDetail/UIDetail.js +++ /dev/null @@ -1,119 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { Link, useHistory, useLocation } from 'react-router-dom'; - -import { t } from '@lingui/macro'; -import { Button } from '@patternfly/react-core'; -import { CaretLeftIcon } from '@patternfly/react-icons'; -import { CardBody, CardActionsRow } from 'components/Card'; -import ContentLoading from 'components/ContentLoading'; -import ContentError from 'components/ContentError'; -import RoutedTabs from 'components/RoutedTabs'; -import { SettingsAPI } from 'api'; -import useRequest from 'hooks/useRequest'; -import { DetailList } from 'components/DetailList'; -import { useConfig } from 'contexts/Config'; -import { useSettings } from 'contexts/Settings'; -import { pluck } from '../../shared/settingUtils'; -import { SettingDetail } from '../../shared'; - -function UIDetail() { - const { me } = useConfig(); - const { GET: options } = useSettings(); - const history = useHistory(); - const { hardReload } = useLocation(); - - if (hardReload) { - history.go(); - } - - const { - isLoading, - error, - request, - result: ui, - } = useRequest( - useCallback(async () => { - const { data } = await SettingsAPI.readCategory('ui'); - - const uiData = pluck( - data, - 'PENDO_TRACKING_STATE', - 'CUSTOM_LOGO', - 'CUSTOM_LOGIN_INFO' - ); - - return uiData; - }, []), - null - ); - - useEffect(() => { - request(); - }, [request]); - - const tabsArray = [ - { - name: ( - <> - - {t`Back to Settings`} - - ), - link: `/settings`, - id: 99, - }, - { - name: t`Details`, - link: `/settings/ui/details`, - id: 0, - }, - ]; - - // Change CUSTOM_LOGO type from string to image - // to help SettingDetail render it as an - if (options?.CUSTOM_LOGO) { - options.CUSTOM_LOGO.type = 'image'; - } - - return ( - <> - - - {isLoading && } - {!isLoading && error && } - {!isLoading && ui && ( - - {Object.keys(ui).map((key) => { - const record = options?.[key]; - return ( - - ); - })} - - )} - {me?.is_superuser && ( - - - - )} - - - ); -} - -export default UIDetail; diff --git a/awx/ui/src/screens/Setting/UI/UIDetail/UIDetail.test.js b/awx/ui/src/screens/Setting/UI/UIDetail/UIDetail.test.js deleted file mode 100644 index 39249cfb051d..000000000000 --- a/awx/ui/src/screens/Setting/UI/UIDetail/UIDetail.test.js +++ /dev/null @@ -1,95 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { SettingsProvider } from 'contexts/Settings'; -import { SettingsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../../testUtils/enzymeHelpers'; -import { assertDetail } from '../../shared/settingTestUtils'; -import mockAllOptions from '../../shared/data.allSettingOptions.json'; -import UIDetail from './UIDetail'; - -jest.mock('../../../../api'); - -describe('', () => { - let wrapper; - - beforeEach(() => { - SettingsAPI.readCategory.mockResolvedValue({ - data: { - CUSTOM_LOGIN_INFO: 'mock info', - CUSTOM_LOGO: 'data:image/png', - PENDO_TRACKING_STATE: 'off', - }, - }); - }); - - beforeEach(async () => { - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - afterAll(() => { - jest.clearAllMocks(); - }); - - test('initially renders without crashing', () => { - expect(wrapper.find('UIDetail').length).toBe(1); - }); - - test('should render expected tabs', () => { - const expectedTabs = ['Back to Settings', 'Details']; - wrapper.find('RoutedTabs li').forEach((tab, index) => { - expect(tab.text()).toEqual(expectedTabs[index]); - }); - }); - - test('should render expected details', () => { - assertDetail(wrapper, 'User Analytics Tracking State', 'off'); - assertDetail(wrapper, 'Custom Login Info', 'mock info'); - expect(wrapper.find('Detail[label="Custom Logo"] dt').text()).toBe( - 'Custom Logo' - ); - expect(wrapper.find('Detail[label="Custom Logo"] dd img').length).toBe(1); - }); - - test('should hide edit button from non-superusers', async () => { - const config = { - me: { - is_superuser: false, - }, - }; - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { config }, - } - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('Button[aria-label="Edit"]').exists()).toBeFalsy(); - }); - - test('should display content error when api throws error on initial render', async () => { - SettingsAPI.readCategory.mockRejectedValue(new Error()); - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/UI/UIDetail/index.js b/awx/ui/src/screens/Setting/UI/UIDetail/index.js deleted file mode 100644 index 791d1d88734d..000000000000 --- a/awx/ui/src/screens/Setting/UI/UIDetail/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './UIDetail'; diff --git a/awx/ui/src/screens/Setting/UI/UIEdit/UIEdit.js b/awx/ui/src/screens/Setting/UI/UIEdit/UIEdit.js deleted file mode 100644 index a86d94829312..000000000000 --- a/awx/ui/src/screens/Setting/UI/UIEdit/UIEdit.js +++ /dev/null @@ -1,152 +0,0 @@ -import React, { useCallback, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { Formik } from 'formik'; -import { Form } from '@patternfly/react-core'; -import { CardBody } from 'components/Card'; -import ContentError from 'components/ContentError'; -import ContentLoading from 'components/ContentLoading'; -import { FormSubmitError } from 'components/FormField'; -import { FormColumnLayout } from 'components/FormLayout'; -import { useSettings } from 'contexts/Settings'; -import { useConfig } from 'contexts/Config'; -import useModal from 'hooks/useModal'; -import useRequest from 'hooks/useRequest'; -import { SettingsAPI } from 'api'; -import { - ChoiceField, - FileUploadField, - TextAreaField, -} from '../../shared/SharedFields'; -import { RevertAllAlert, RevertFormActionGroup } from '../../shared'; - -function UIEdit() { - const history = useHistory(); - const { isModalOpen, toggleModal, closeModal } = useModal(); - const { PUT: options } = useSettings(); - const { license_info } = useConfig(); - - const { - isLoading, - error, - request: fetchUI, - result: uiData, - } = useRequest( - useCallback(async () => { - const { data } = await SettingsAPI.readCategory('ui'); - const mergedData = {}; - Object.keys(data).forEach((key) => { - if (!options[key]) { - return; - } - mergedData[key] = options[key]; - mergedData[key].value = data[key]; - }); - return mergedData; - }, [options]), - null - ); - - useEffect(() => { - fetchUI(); - }, [fetchUI]); - - const { error: submitError, request: submitForm } = useRequest( - useCallback( - async (values) => { - await SettingsAPI.updateAll(values); - if ( - values?.PENDO_TRACKING_STATE !== uiData?.PENDO_TRACKING_STATE?.value - ) { - history.push({ - pathname: '/settings/ui/details', - hardReload: true, - }); - } else { - history.push('/settings/ui/details'); - } - }, - [history, uiData] - ), - null - ); - - const { error: revertError, request: revertAll } = useRequest( - useCallback(async () => { - await SettingsAPI.revertCategory('ui'); - }, []), - null - ); - - const handleSubmit = async (form) => { - await submitForm(form); - }; - - const handleRevertAll = async () => { - await revertAll(); - - closeModal(); - - history.push({ - pathname: '/settings/ui/details', - hardReload: true, - }); - }; - - const handleCancel = () => { - history.push('/settings/ui/details'); - }; - - return ( - - {isLoading && } - {!isLoading && error && } - {!isLoading && uiData && ( - - {(formik) => ( -
- - - - - {submitError && } - {revertError && } - - - {isModalOpen && ( - - )} - - )} -
- )} -
- ); -} - -export default UIEdit; diff --git a/awx/ui/src/screens/Setting/UI/UIEdit/UIEdit.test.js b/awx/ui/src/screens/Setting/UI/UIEdit/UIEdit.test.js deleted file mode 100644 index fa3f0f57adf3..000000000000 --- a/awx/ui/src/screens/Setting/UI/UIEdit/UIEdit.test.js +++ /dev/null @@ -1,165 +0,0 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { createMemoryHistory } from 'history'; -import { SettingsProvider } from 'contexts/Settings'; -import { SettingsAPI } from 'api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../../testUtils/enzymeHelpers'; -import mockAllOptions from '../../shared/data.allSettingOptions.json'; -import UIEdit from './UIEdit'; - -jest.mock('../../../../api'); - -describe('', () => { - let wrapper; - let history; - - beforeEach(() => { - SettingsAPI.revertCategory.mockResolvedValue({}); - SettingsAPI.updateAll.mockResolvedValue({}); - SettingsAPI.readCategory.mockResolvedValue({ - data: { - CUSTOM_LOGIN_INFO: 'mock info', - CUSTOM_LOGO: 'data:mock/jpeg;', - PENDO_TRACKING_STATE: 'detailed', - }, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - beforeEach(async () => { - history = createMemoryHistory({ - initialEntries: ['/settings/ui/edit'], - }); - await act(async () => { - wrapper = mountWithContexts( - - - , - { - context: { router: { history } }, - } - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - }); - - test('initially renders without crashing', () => { - expect(wrapper.find('UIEdit').length).toBe(1); - }); - - test('should display expected form fields', async () => { - expect(wrapper.find('FormGroup[label="Custom Login Info"]').length).toBe(1); - expect(wrapper.find('FormGroup[label="Custom Logo"]').length).toBe(1); - expect( - wrapper.find('FormGroup[label="User Analytics Tracking State"]').length - ).toBe(1); - }); - - test('should successfully send default values to api on form revert all', async () => { - expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(0); - expect(wrapper.find('RevertAllAlert')).toHaveLength(0); - await act(async () => { - wrapper - .find('button[aria-label="Revert all to default"]') - .invoke('onClick')(); - }); - wrapper.update(); - expect(wrapper.find('RevertAllAlert')).toHaveLength(1); - await act(async () => { - wrapper - .find('RevertAllAlert button[aria-label="Confirm revert all"]') - .invoke('onClick')(); - }); - wrapper.update(); - expect(SettingsAPI.revertCategory).toHaveBeenCalledTimes(1); - expect(SettingsAPI.revertCategory).toHaveBeenCalledWith('ui'); - }); - - test('should successfully send request to api on form submission', async () => { - act(() => { - wrapper.find('textarea#CUSTOM_LOGIN_INFO').simulate('change', { - target: { value: 'new login info', name: 'CUSTOM_LOGIN_INFO' }, - }); - wrapper - .find('FormGroup[fieldId="CUSTOM_LOGO"] button[aria-label="Revert"]') - .invoke('onClick')(); - }); - wrapper.update(); - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1); - expect(SettingsAPI.updateAll).toHaveBeenCalledWith({ - CUSTOM_LOGIN_INFO: 'new login info', - CUSTOM_LOGO: '', - PENDO_TRACKING_STATE: 'detailed', - }); - }); - - test('should navigate to ui detail on successful submission', async () => { - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - expect(history.location.pathname).toEqual('/settings/ui/details'); - expect(history.location.hardReload).toEqual(undefined); - }); - - test('should navigate to ui detail with reload param on successful submission where PENDO_TRACKING_STATE changes', async () => { - act(() => { - wrapper.find('select#PENDO_TRACKING_STATE').simulate('change', { - target: { value: 'off', name: 'CUSTOM_LOGIN_INFO' }, - }); - }); - wrapper.update(); - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - expect(history.location.pathname).toEqual('/settings/ui/details'); - expect(history.location.hardReload).toEqual(true); - }); - - test('should navigate to ui detail when cancel is clicked', async () => { - await act(async () => { - wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); - }); - expect(history.location.pathname).toEqual('/settings/ui/details'); - }); - - test('should display error message on unsuccessful submission', async () => { - const error = { - response: { - data: { detail: 'An error occurred' }, - }, - }; - SettingsAPI.updateAll.mockImplementation(() => Promise.reject(error)); - expect(wrapper.find('FormSubmitError').length).toBe(0); - expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(0); - await act(async () => { - wrapper.find('Form').invoke('onSubmit')(); - }); - wrapper.update(); - expect(wrapper.find('FormSubmitError').length).toBe(1); - expect(SettingsAPI.updateAll).toHaveBeenCalledTimes(1); - }); - - test('should display ContentError on throw', async () => { - SettingsAPI.readCategory.mockImplementationOnce(() => - Promise.reject(new Error()) - ); - await act(async () => { - wrapper = mountWithContexts( - - - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - expect(wrapper.find('ContentError').length).toBe(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/UI/UIEdit/index.js b/awx/ui/src/screens/Setting/UI/UIEdit/index.js deleted file mode 100644 index affad29bf8da..000000000000 --- a/awx/ui/src/screens/Setting/UI/UIEdit/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './UIEdit'; diff --git a/awx/ui/src/screens/Setting/UI/index.js b/awx/ui/src/screens/Setting/UI/index.js deleted file mode 100644 index a33b447adfa2..000000000000 --- a/awx/ui/src/screens/Setting/UI/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './UI'; diff --git a/awx/ui/src/screens/Setting/index.js b/awx/ui/src/screens/Setting/index.js deleted file mode 100644 index 63a5e968e443..000000000000 --- a/awx/ui/src/screens/Setting/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './Settings'; diff --git a/awx/ui/src/screens/Setting/shared/RevertAllAlert.js b/awx/ui/src/screens/Setting/shared/RevertAllAlert.js deleted file mode 100644 index 496f71731827..000000000000 --- a/awx/ui/src/screens/Setting/shared/RevertAllAlert.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; - -import { t } from '@lingui/macro'; -import { Button } from '@patternfly/react-core'; -import AlertModal from 'components/AlertModal'; - -function RevertAllAlert({ onClose, onRevertAll }) { - return ( - - {t`Revert all`} - , - , - ]} - > - {t`This will revert all configuration values on this page to - their factory defaults. Are you sure you want to proceed?`} - - ); -} - -export default RevertAllAlert; diff --git a/awx/ui/src/screens/Setting/shared/RevertAllAlert.test.js b/awx/ui/src/screens/Setting/shared/RevertAllAlert.test.js deleted file mode 100644 index dea34403ccb3..000000000000 --- a/awx/ui/src/screens/Setting/shared/RevertAllAlert.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; -import RevertAllAlert from './RevertAllAlert'; - -describe('RevertAllAlert', () => { - test('renders the expected content', async () => { - const wrapper = mountWithContexts( - {}} onRevertAll={() => {}} /> - ); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/shared/RevertButton.js b/awx/ui/src/screens/Setting/shared/RevertButton.js deleted file mode 100644 index 9a0eb40f7117..000000000000 --- a/awx/ui/src/screens/Setting/shared/RevertButton.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; -import { useField } from 'formik'; -import { Button, Tooltip } from '@patternfly/react-core'; -import styled from 'styled-components'; - -const ButtonWrapper = styled.div` - margin-left: auto; - &&& { - --pf-c-button--FontSize: var(--pf-c-button--m-small--FontSize); - } -`; - -function RevertButton({ - id, - defaultValue, - isDisabled = false, - onRevertCallback = () => null, -}) { - const [field, meta, helpers] = useField(id); - const initialValue = meta.initialValue ?? ''; - const currentValue = field.value; - let isRevertable = true; - let isMatch = false; - - if (currentValue === defaultValue && currentValue !== initialValue) { - isRevertable = false; - } - - if (currentValue === defaultValue && currentValue === initialValue) { - isMatch = true; - } - - const handleConfirm = () => { - helpers.setValue(isRevertable ? defaultValue : initialValue); - onRevertCallback(); - }; - - const revertTooltipContent = isRevertable - ? t`Revert to factory default.` - : t`Restore initial value.`; - const tooltipContent = - isDisabled || isMatch - ? t`Setting matches factory default.` - : revertTooltipContent; - - return ( - - - - - - ); -} - -RevertButton.propTypes = { - id: PropTypes.string.isRequired, -}; - -export default RevertButton; diff --git a/awx/ui/src/screens/Setting/shared/RevertButton.test.js b/awx/ui/src/screens/Setting/shared/RevertButton.test.js deleted file mode 100644 index e2801843fe7f..000000000000 --- a/awx/ui/src/screens/Setting/shared/RevertButton.test.js +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { Formik } from 'formik'; -import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; -import RevertButton from './RevertButton'; - -describe('RevertButton', () => { - let wrapper; - - test('button text should display "Revert"', async () => { - wrapper = mountWithContexts( - - - - ); - expect(wrapper.find('button').text()).toEqual('Revert'); - }); - - test('button text should display "Undo"', async () => { - wrapper = mountWithContexts( - - - - ); - expect(wrapper.find('button').text()).toEqual('Revert'); - }); - - test('should revert value to default on button click', async () => { - wrapper = mountWithContexts( - - - - ); - expect(wrapper.find('button').text()).toEqual('Revert'); - await act(async () => { - wrapper.find('button[aria-label="Revert"]').invoke('onClick')(); - }); - wrapper.update(); - expect(wrapper.find('button').text()).toEqual('Undo'); - }); - - test('should be disabled when current value equals the initial and default values', async () => { - wrapper = mountWithContexts( - - - - ); - expect(wrapper.find('button').text()).toEqual('Revert'); - expect(wrapper.find('button').props().disabled).toBe(true); - }); -}); diff --git a/awx/ui/src/screens/Setting/shared/RevertFormActionGroup.js b/awx/ui/src/screens/Setting/shared/RevertFormActionGroup.js deleted file mode 100644 index 7d5d213bbf4c..000000000000 --- a/awx/ui/src/screens/Setting/shared/RevertFormActionGroup.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { t } from '@lingui/macro'; -import { ActionGroup, Button } from '@patternfly/react-core'; -import { FormFullWidthLayout } from 'components/FormLayout'; - -const RevertFormActionGroup = ({ children, onCancel, onRevert, onSubmit }) => ( - - - - - {children} - - - -); - -RevertFormActionGroup.propTypes = { - onCancel: PropTypes.func.isRequired, - onRevert: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, -}; - -export default RevertFormActionGroup; diff --git a/awx/ui/src/screens/Setting/shared/RevertFormActionGroup.test.js b/awx/ui/src/screens/Setting/shared/RevertFormActionGroup.test.js deleted file mode 100644 index 658491c5b361..000000000000 --- a/awx/ui/src/screens/Setting/shared/RevertFormActionGroup.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; -import RevertFormActionGroup from './RevertFormActionGroup'; - -describe('RevertFormActionGroup', () => { - test('should render the expected content', () => { - const wrapper = mountWithContexts( - {}} - onCancel={() => {}} - onRevert={() => {}} - /> - ); - expect(wrapper).toHaveLength(1); - }); -}); diff --git a/awx/ui/src/screens/Setting/shared/SettingDetail.js b/awx/ui/src/screens/Setting/shared/SettingDetail.js deleted file mode 100644 index c133bfbe065c..000000000000 --- a/awx/ui/src/screens/Setting/shared/SettingDetail.js +++ /dev/null @@ -1,131 +0,0 @@ -import React from 'react'; - -import { t } from '@lingui/macro'; -import { Detail } from 'components/DetailList'; -import CodeDetail from 'components/DetailList/CodeDetail'; - -function sortObj(obj) { - if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) { - return obj; - } - const sorted = {}; - Object.keys(obj) - .sort() - .forEach((key) => { - sorted[key] = sortObj(obj[key]); - }); - return sorted; -} - -export default ({ helpText, id, label, type, unit = '', value }) => { - const dataType = value === '$encrypted$' ? 'encrypted' : type; - let detail = null; - - switch (dataType) { - case 'nested object': - detail = ( - - ); - break; - case 'list': - detail = ( - - ); - break; - case 'certificate': - detail = ( - - ); - break; - case 'image': - detail = ( - - ) - } - /> - ); - break; - case 'encrypted': - detail = ( - - ); - break; - case 'boolean': - detail = ( - - ); - break; - case 'choice': - case 'field': - case 'string': - detail = ( - - ); - break; - case 'integer': - detail = ( - - ); - break; - default: - detail = null; - } - return detail; -}; diff --git a/awx/ui/src/screens/Setting/shared/SharedFields.js b/awx/ui/src/screens/Setting/shared/SharedFields.js deleted file mode 100644 index 06851e3b9e95..000000000000 --- a/awx/ui/src/screens/Setting/shared/SharedFields.js +++ /dev/null @@ -1,562 +0,0 @@ -import React, { useState } from 'react'; -import { shape, string } from 'prop-types'; -import { t } from '@lingui/macro'; -import { useField } from 'formik'; -import { - Button, - FileUpload, - FormGroup as PFFormGroup, - InputGroup, - Switch, - TextArea, - TextInput, - Tooltip, - ButtonVariant, -} from '@patternfly/react-core'; -import FileUploadIcon from '@patternfly/react-icons/dist/js/icons/file-upload-icon'; -import { ExclamationCircleIcon as PFExclamationCircleIcon } from '@patternfly/react-icons'; -import styled from 'styled-components'; -import AnsibleSelect from 'components/AnsibleSelect'; -import { ExecutionEnvironmentLookup } from 'components/Lookup'; -import CodeEditor from 'components/CodeEditor'; -import { PasswordInput } from 'components/FormField'; -import { FormFullWidthLayout } from 'components/FormLayout'; -import Popover from 'components/Popover'; -import { combine, minMaxValue, required, url, number } from 'util/validators'; -import AlertModal from 'components/AlertModal'; -import RevertButton from './RevertButton'; - -const ExclamationCircleIcon = styled(PFExclamationCircleIcon)` - && { - color: var(--pf-global--danger-color--100); - } -`; - -const FormGroup = styled(PFFormGroup)` - .pf-c-form__group-label { - display: inline-flex; - align-items: center; - width: 100%; - } -`; - -const Selected = styled.div` - display: flex; - justify-content: space-between; - background-color: white; - border-bottom-color: var(--pf-global--BorderColor--200); -`; - -const SettingGroup = ({ - children, - defaultValue, - fieldId, - helperTextInvalid, - isDisabled, - isRequired, - label, - onRevertCallback, - popoverContent, - validated, -}) => ( - - - - - } - > - {children} - -); -const BooleanField = ({ - ariaLabel = '', - name, - config, - disabled = false, - needsConfirmationModal, - modalTitle, -}) => { - const [field, meta, helpers] = useField(name); - const [isModalOpen, setIsModalOpen] = useState(false); - - return config ? ( - - {isModalOpen && ( - { - setIsModalOpen(false); - }} - actions={[ - , - , - ]} - >{t`Are you sure you want to disable local authentication? Doing so could impact users' ability to log in and the system administrator's ability to reverse this change.`} - )} - { - if (needsConfirmationModal && isOn) { - setIsModalOpen(true); - } - helpers.setValue(!field.value); - }} - aria-label={ariaLabel || config.label} - /> - - ) : null; -}; -BooleanField.propTypes = { - name: string.isRequired, - config: shape({}).isRequired, -}; - -const ChoiceField = ({ name, config, isRequired = false }) => { - const validate = isRequired ? required(null) : null; - const [field, meta] = useField({ name, validate }); - const isValid = !meta.error || !meta.touched; - - return config ? ( - - ({ - label, - value: value ?? '', - key: value ?? index, - })), - ]} - /> - - ) : null; -}; -ChoiceField.propTypes = { - name: string.isRequired, - config: shape({}).isRequired, -}; - -const EncryptedField = ({ name, config, isRequired = false }) => { - const validate = isRequired ? required(null) : null; - const [, meta] = useField({ name, validate }); - const isValid = !(meta.touched && meta.error); - - return config ? ( - - - - - - ) : null; -}; -EncryptedField.propTypes = { - name: string.isRequired, - config: shape({}).isRequired, -}; - -const ExecutionEnvField = ({ name, config, isRequired = false }) => { - const [field, meta, helpers] = useField({ name }); - return config ? ( - helpers.setValue(config.default)} - > - { - helpers.setValue(value, false); - }} - overrideLabel - fieldName={name} - /> - - ) : null; -}; -ExecutionEnvField.propTypes = { - name: string.isRequired, - config: shape({}).isRequired, -}; - -const InputAlertField = ({ name, config }) => { - const [field, meta] = useField({ name }); - const isValid = !(meta.touched && meta.error); - const [isModalOpen, setIsModalOpen] = useState(false); - const [isDisable, setIsDisable] = useState(true); - - const handleSetIsOpen = () => { - setIsModalOpen(true); - }; - - const handleEnableTextInput = () => { - setIsDisable(false); - }; - - return config ? ( - <> - - - {isDisable && ( - - - - )} - { - field.onChange(event); - }} - isDisabled={isDisable} - /> - - - {isModalOpen && isDisable && ( - { - setIsModalOpen(false); - }} - actions={[ - , - , - ]} - > - {t`Are you sure you want to edit login redirect override URL? Doing so could impact users' ability to log in to the system once local authentication is also disabled.`} - - )} - - ) : null; -}; - -InputAlertField.propTypes = { - name: string.isRequired, - config: shape({}).isRequired, -}; - -const InputField = ({ name, config, type = 'text', isRequired = false }) => { - const min_value = config?.min_value ?? Number.MIN_SAFE_INTEGER; - const max_value = config?.max_value ?? Number.MAX_SAFE_INTEGER; - const validators = [ - ...(isRequired ? [required(null)] : []), - ...(type === 'url' ? [url()] : []), - ...(type === 'number' ? [number(), minMaxValue(min_value, max_value)] : []), - ]; - const [field, meta] = useField({ name, validate: combine(validators) }); - const isValid = !(meta.touched && meta.error); - - return config ? ( - - { - field.onChange(event); - }} - /> - - ) : null; -}; -InputField.propTypes = { - name: string.isRequired, - config: shape({}), -}; -InputField.defaultProps = { - config: null, -}; - -const TextAreaField = ({ name, config, isRequired = false }) => { - const validate = isRequired ? required(null) : null; - const [field, meta] = useField({ name, validate }); - const isValid = !(meta.touched && meta.error); - - return config ? ( - - \n
\n
\n )\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/curl.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const DeepLink = ({ enabled, path, text }) => {\n return (\n e.preventDefault() : null}\n href={enabled ? `#/${path}` : null}>\n {text}\n \n )\n}\nDeepLink.propTypes = {\n enabled: PropTypes.bool,\n isShown: PropTypes.bool,\n path: PropTypes.string,\n text: PropTypes.string\n}\n\nexport default DeepLink\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/deep-link.jsx","import React from \"react\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst EnumModel = ({ value, getComponent }) => {\n let ModelCollapse = getComponent(\"ModelCollapse\")\n let collapsedContent = Array [ { value.count() } ]\n return \n Enum:
\n \n [ { value.join(\", \") } ]\n \n
\n}\nEnumModel.propTypes = {\n value: ImPropTypes.iterable,\n getComponent: ImPropTypes.func\n}\n\nexport default EnumModel\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/enum-model.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List } from \"immutable\"\nimport { Collapse } from \"react-collapse\"\n\nexport default class Errors extends React.Component {\n\n static propTypes = {\n editorActions: PropTypes.object,\n errSelectors: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired\n }\n\n render() {\n let { editorActions, errSelectors, layoutSelectors, layoutActions } = this.props\n\n if(editorActions && editorActions.jumpToLine) {\n var jumpToLine = editorActions.jumpToLine\n }\n\n let errors = errSelectors.allErrors()\n\n // all thrown errors, plus error-level everything else\n let allErrorsToDisplay = errors.filter(err => err.get(\"type\") === \"thrown\" ? true :err.get(\"level\") === \"error\")\n\n if(!allErrorsToDisplay || allErrorsToDisplay.count() < 1) {\n return null\n }\n\n let isVisible = layoutSelectors.isShown([\"errorPane\"], true)\n let toggleVisibility = () => layoutActions.show([\"errorPane\"], !isVisible)\n\n let sortedJSErrors = allErrorsToDisplay.sortBy(err => err.get(\"line\"))\n\n return (\n
\n        
\n

Errors

\n \n
\n \n
\n { sortedJSErrors.map((err, i) => {\n let type = err.get(\"type\")\n if(type === \"thrown\" || type === \"auth\") {\n return \n }\n if(type === \"spec\") {\n return \n }\n }) }\n
\n
\n
\n )\n }\n}\n\nconst ThrownErrorItem = ( { error, jumpToLine } ) => {\n if(!error) {\n return null\n }\n let errorLine = error.get(\"line\")\n\n return (\n
\n { !error ? null :\n
\n

{ (error.get(\"source\") && error.get(\"level\")) ?\n toTitleCase(error.get(\"source\")) + \" \" + error.get(\"level\") : \"\" }\n { error.get(\"path\") ? at {error.get(\"path\")}: null }

\n \n { error.get(\"message\") }\n \n
\n { errorLine && jumpToLine ? Jump to line { errorLine } : null }\n
\n
\n }\n
\n )\n }\n\nconst SpecErrorItem = ( { error, jumpToLine } ) => {\n let locationMessage = null\n\n if(error.get(\"path\")) {\n if(List.isList(error.get(\"path\"))) {\n locationMessage = at { error.get(\"path\").join(\".\") }\n } else {\n locationMessage = at { error.get(\"path\") }\n }\n } else if(error.get(\"line\") && !jumpToLine) {\n locationMessage = on line { error.get(\"line\") }\n }\n\n return (\n
\n { !error ? null :\n
\n

{ toTitleCase(error.get(\"source\")) + \" \" + error.get(\"level\") } { locationMessage }

\n { error.get(\"message\") }\n
\n { jumpToLine ? (\n Jump to line { error.get(\"line\") }\n ) : null }\n
\n
\n }\n
\n )\n }\n\nfunction toTitleCase(str) {\n return (str || \"\")\n .split(\" \")\n .map(substr => substr[0].toUpperCase() + substr.slice(1))\n .join(\" \")\n}\n\nThrownErrorItem.propTypes = {\n error: PropTypes.object.isRequired,\n jumpToLine: PropTypes.func\n}\n\nThrownErrorItem.defaultProps = {\n jumpToLine: null\n}\n\nSpecErrorItem.propTypes = {\n error: PropTypes.object.isRequired,\n jumpToLine: PropTypes.func\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/errors.jsx","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class Execute extends Component {\n\n static propTypes = {\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n operation: PropTypes.object.isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n onExecute: PropTypes.func\n }\n\n onClick=()=>{\n let { specSelectors, specActions, operation, path, method } = this.props\n\n specActions.validateParams( [path, method] )\n\n if ( specSelectors.validateBeforeExecute([path, method]) ) {\n if(this.props.onExecute) {\n this.props.onExecute()\n }\n specActions.execute( { operation, path, method } )\n }\n }\n\n onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)\n\n render(){\n return (\n \n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/execute.jsx","import React from \"react\"\n\nexport default class Footer extends React.Component {\n render() {\n return (\n
\n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/footer.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Im from \"immutable\"\n\nconst propStyle = { color: \"#999\", fontStyle: \"italic\" }\n\nexport default class Headers extends React.Component {\n\n static propTypes = {\n headers: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n };\n\n render() {\n\n let { headers, getComponent } = this.props\n const Property = getComponent(\"Property\")\n\n if ( !headers || !headers.size )\n return null\n\n return (\n
\n

Headers:

\n \n \n \n \n \n \n \n \n \n {\n headers.entrySeq().map( ([ key, header ]) => {\n if(!Im.Map.isMap(header)) {\n return null\n }\n const type = header.getIn([\"schema\"]) ? header.getIn([\"schema\", \"type\"]) : header.getIn([\"type\"])\n const schemaExample = header.getIn([\"schema\", \"example\"])\n\n return (\n \n \n \n )\n }).toArray()\n }\n \n
NameDescriptionType
{ key }{ header.get( \"description\" ) }{ type } { schemaExample ? : null }
\n
\n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/headers.jsx","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { highlight } from \"core/utils\"\n\nexport default class HighlightCode extends Component {\n static propTypes = {\n value: PropTypes.string.isRequired,\n className: PropTypes.string\n }\n\n componentDidMount() {\n highlight(this.el)\n }\n\n componentDidUpdate() {\n highlight(this.el)\n }\n\n initializeComponent = (c) => {\n this.el = c\n }\n\n render () {\n let { value, className } = this.props\n className = className || \"\"\n\n return
{ value }
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/highlight-code.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { fromJS } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { sanitizeUrl } from \"core/utils\"\n\n\nclass Path extends React.Component {\n static propTypes = {\n host: PropTypes.string,\n basePath: PropTypes.string\n }\n\n render() {\n let { host, basePath } = this.props\n\n return (\n
\n        [ Base URL: {host}{basePath} ]\n      
\n )\n }\n}\n\n\nclass Contact extends React.Component {\n static propTypes = {\n data: PropTypes.object\n }\n\n render(){\n let { data } = this.props\n let name = data.get(\"name\") || \"the developer\"\n let url = data.get(\"url\")\n let email = data.get(\"email\")\n\n return (\n \n )\n }\n}\n\nclass License extends React.Component {\n static propTypes = {\n license: PropTypes.object\n }\n\n render(){\n let { license } = this.props\n let name = license.get(\"name\") || \"License\"\n let url = license.get(\"url\")\n\n return (\n
\n {\n url ? { name }\n : { name }\n }\n
\n )\n }\n}\n\nexport default class Info extends React.Component {\n static propTypes = {\n info: PropTypes.object,\n url: PropTypes.string,\n host: PropTypes.string,\n basePath: PropTypes.string,\n externalDocs: ImPropTypes.map,\n getComponent: PropTypes.func.isRequired,\n }\n\n render() {\n let { info, url, host, basePath, getComponent, externalDocs } = this.props\n let version = info.get(\"version\")\n let description = info.get(\"description\")\n let title = info.get(\"title\")\n let termsOfService = info.get(\"termsOfService\")\n let contact = info.get(\"contact\")\n let license = info.get(\"license\")\n const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()\n\n const Markdown = getComponent(\"Markdown\")\n const VersionStamp = getComponent(\"VersionStamp\")\n\n return (\n
\n
\n

{ title }\n { version && }\n

\n { host || basePath ? : null }\n { url && { url } }\n
\n\n
\n \n
\n\n {\n termsOfService && \n }\n\n { contact && contact.size ? : null }\n { license && license.size ? : null }\n { externalDocsUrl ?\n {externalDocsDescription || externalDocsUrl}\n : null }\n\n
\n )\n }\n\n}\n\nInfo.propTypes = {\n title: PropTypes.any,\n description: PropTypes.any,\n version: PropTypes.any,\n url: PropTypes.string\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/info.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class BaseLayout extends React.Component {\n\n static propTypes = {\n errSelectors: PropTypes.object.isRequired,\n errActions: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n }\n\n onFilterChange =(e) => {\n let {target: {value}} = e\n this.props.layoutActions.updateFilter(value)\n }\n\n render() {\n let {\n specSelectors,\n specActions,\n getComponent,\n layoutSelectors,\n oas3Selectors,\n oas3Actions\n } = this.props\n\n let info = specSelectors.info()\n let url = specSelectors.url()\n let basePath = specSelectors.basePath()\n let host = specSelectors.host()\n let securityDefinitions = specSelectors.securityDefinitions()\n let externalDocs = specSelectors.externalDocs()\n let schemes = specSelectors.schemes()\n let servers = specSelectors.servers()\n\n let Info = getComponent(\"info\")\n let Operations = getComponent(\"operations\", true)\n let Models = getComponent(\"Models\", true)\n let AuthorizeBtn = getComponent(\"authorizeBtn\", true)\n let Row = getComponent(\"Row\")\n let Col = getComponent(\"Col\")\n let Servers = getComponent(\"Servers\")\n let Errors = getComponent(\"errors\", true)\n\n let isLoading = specSelectors.loadingStatus() === \"loading\"\n let isFailed = specSelectors.loadingStatus() === \"failed\"\n let filter = layoutSelectors.currentFilter()\n\n let inputStyle = {}\n if(isFailed) inputStyle.color = \"red\"\n if(isLoading) inputStyle.color = \"#aaa\"\n\n const Schemes = getComponent(\"schemes\")\n\n const isSpecEmpty = !specSelectors.specStr()\n\n if(isSpecEmpty) {\n let loadingMessage\n if(isLoading) {\n loadingMessage =
\n } else {\n loadingMessage =

No API definition provided.

\n }\n\n return
\n
\n {loadingMessage}\n
\n
\n }\n\n return (\n\n
\n
\n \n \n \n { info.count() ? (\n \n ) : null }\n \n \n { schemes && schemes.size || securityDefinitions ? (\n
\n \n { schemes && schemes.size ? (\n \n ) : null }\n\n { securityDefinitions ? (\n \n ) : null }\n \n
\n ) : null }\n\n { servers && servers.size ? (\n
\n \n Server\n \n \n
\n\n ) : null}\n\n {\n filter === null || filter === false ? null :\n
\n \n \n \n
\n }\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/layouts/base.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { Iterable } from \"immutable\"\n\nconst Headers = ( { headers } )=>{\n return (\n
\n
Response headers
\n
{headers}
\n
)\n}\nHeaders.propTypes = {\n headers: PropTypes.array.isRequired\n}\n\nconst Duration = ( { duration } ) => {\n return (\n
\n
Request duration
\n
{duration} ms
\n
\n )\n}\nDuration.propTypes = {\n duration: PropTypes.number.isRequired\n}\n\n\nexport default class LiveResponse extends React.Component {\n static propTypes = {\n response: PropTypes.instanceOf(Iterable).isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n displayRequestDuration: PropTypes.bool.isRequired,\n specSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired\n }\n\n shouldComponentUpdate(nextProps) {\n // BUG: props.response is always coming back as a new Immutable instance\n // same issue as responses.jsx (tryItOutResponse)\n return this.props.response !== nextProps.response\n || this.props.path !== nextProps.path\n || this.props.method !== nextProps.method\n || this.props.displayRequestDuration !== nextProps.displayRequestDuration\n }\n\n render() {\n const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props\n const { showMutatedRequest } = getConfigs()\n\n const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)\n const status = response.get(\"status\")\n const url = response.get(\"url\")\n const headers = response.get(\"headers\").toJS()\n const notDocumented = response.get(\"notDocumented\")\n const isError = response.get(\"error\")\n const body = response.get(\"text\")\n const duration = response.get(\"duration\")\n const headersKeys = Object.keys(headers)\n const contentType = headers[\"content-type\"]\n\n const Curl = getComponent(\"curl\")\n const ResponseBody = getComponent(\"responseBody\")\n const returnObject = headersKeys.map(key => {\n return {key}: {headers[key]} \n })\n const hasHeaders = returnObject.length !== 0\n\n return (\n
\n { curlRequest && }\n { url &&
\n

Request URL

\n
\n
{url}
\n
\n
\n }\n

Server response

\n \n \n \n \n \n \n \n \n \n \n \n \n \n
CodeDetails
\n { status }\n {\n notDocumented ?
\n Undocumented \n
\n : null\n }\n
\n {\n isError ? \n {`${response.get(\"name\")}: ${response.get(\"message\")}`}\n \n : null\n }\n {\n body ? \n : null\n }\n {\n hasHeaders ? : null\n }\n {\n displayRequestDuration && duration ? : null\n }\n
\n
\n )\n }\n\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n response: ImPropTypes.map\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/live-response.jsx","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class ModelCollapse extends Component {\n static propTypes = {\n collapsedContent: PropTypes.any,\n expanded: PropTypes.bool,\n children: PropTypes.any,\n title: PropTypes.element,\n modelName: PropTypes.string,\n onToggle: PropTypes.func\n }\n\n static defaultProps = {\n collapsedContent: \"{...}\",\n expanded: false,\n title: null,\n onToggle: () => {}\n }\n\n constructor(props, context) {\n super(props, context)\n\n let { expanded, collapsedContent } = this.props\n\n this.state = {\n expanded : expanded,\n collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent\n }\n }\n\n componentWillReceiveProps(nextProps){\n\n if(this.props.expanded!= nextProps.expanded){\n this.setState({expanded: nextProps.expanded})\n }\n\n }\n\n toggleCollapsed=()=>{\n\n\n if(this.props.onToggle){\n this.props.onToggle(this.props.modelName,!this.state.expanded)\n }\n\n this.setState({\n expanded: !this.state.expanded\n })\n }\n\n render () {\n const {title} = this.props\n return (\n \n { title && {title} }\n \n \n \n { this.state.expanded ? this.props.children :this.state.collapsedContent }\n \n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/model-collapse.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class ModelExample extends React.Component {\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n schema: PropTypes.object.isRequired,\n example: PropTypes.any.isRequired,\n isExecute: PropTypes.bool,\n getConfigs: PropTypes.func.isRequired,\n specPath: ImPropTypes.list.isRequired,\n }\n\n constructor(props, context) {\n super(props, context)\n let { getConfigs } = this.props\n let { defaultModelRendering } = getConfigs()\n if (defaultModelRendering !== \"example\" && defaultModelRendering !== \"model\") {\n defaultModelRendering = \"example\"\n }\n this.state = {\n activeTab: defaultModelRendering\n }\n }\n\n activeTab =( e ) => {\n let { target : { dataset : { name } } } = e\n\n this.setState({\n activeTab: name\n })\n }\n\n render() {\n let { getComponent, specSelectors, schema, example, isExecute, getConfigs, specPath } = this.props\n let { defaultModelExpandDepth } = getConfigs()\n const ModelWrapper = getComponent(\"ModelWrapper\")\n\n return
\n \n
\n {\n (isExecute || this.state.activeTab === \"example\") && example\n }\n {\n !isExecute && this.state.activeTab === \"model\" && \n\n\n }\n
\n
\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/model-example.jsx","import React, { Component, } from \"react\"\nimport PropTypes from \"prop-types\"\n//import layoutActions from \"actions/layout\"\n\n\nexport default class ModelWrapper extends Component {\n\n\n static propTypes = {\n schema: PropTypes.object.isRequired,\n name: PropTypes.string,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n expandDepth: PropTypes.number,\n layoutActions: PropTypes.object,\n layoutSelectors: PropTypes.object.isRequired\n }\n\n onToggle = (name,isShown) => {\n // If this prop is present, we'll have deepLinking for it\n if(this.props.layoutActions) {\n this.props.layoutActions.show([\"models\", name],isShown)\n }\n }\n\n render(){\n let { getComponent, getConfigs } = this.props\n const Model = getComponent(\"Model\")\n\n let expanded\n if(this.props.layoutSelectors) {\n // If this is prop is present, we'll have deepLinking for it\n expanded = this.props.layoutSelectors.isShown([\"models\",this.props.name])\n }\n\n return
\n \n
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/model-wrapper.jsx","import React, { Component } from \"react\"\nimport Im from \"immutable\"\nimport PropTypes from \"prop-types\"\n\nexport default class Models extends Component {\n static propTypes = {\n getComponent: PropTypes.func,\n specSelectors: PropTypes.object,\n layoutSelectors: PropTypes.object,\n layoutActions: PropTypes.object,\n getConfigs: PropTypes.func.isRequired\n }\n\n render(){\n let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props\n let definitions = specSelectors.definitions()\n let { docExpansion, defaultModelsExpandDepth } = getConfigs()\n if (!definitions.size || defaultModelsExpandDepth < 0) return null\n\n let showModels = layoutSelectors.isShown(\"models\", defaultModelsExpandDepth > 0 && docExpansion !== \"none\")\n const specPathBase = specSelectors.isOAS3() ? [\"components\", \"schemas\"] : [\"definitions\"]\n\n const ModelWrapper = getComponent(\"ModelWrapper\")\n const Collapse = getComponent(\"Collapse\")\n\n return
\n

layoutActions.show(\"models\", !showModels)}>\n Models\n \n \n \n

\n \n {\n definitions.entrySeq().map( ( [ name, model ])=>{\n\n return
\n \n
\n }).toArray()\n }\n
\n
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/models.jsx","import React, { Component, } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst braceOpen = \"{\"\nconst braceClose = \"}\"\n\nexport default class ObjectModel extends Component {\n static propTypes = {\n schema: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n expanded: PropTypes.bool,\n onToggle: PropTypes.func,\n specSelectors: PropTypes.object.isRequired,\n name: PropTypes.string,\n isRef: PropTypes.bool,\n expandDepth: PropTypes.number,\n depth: PropTypes.number,\n specPath: ImPropTypes.list.isRequired\n }\n\n render(){\n let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props\n let { specSelectors,expandDepth } = otherProps\n const { isOAS3 } = specSelectors\n\n if(!schema) {\n return null\n }\n\n const { showExtensions } = getConfigs()\n\n let description = schema.get(\"description\")\n let properties = schema.get(\"properties\")\n let additionalProperties = schema.get(\"additionalProperties\")\n let title = schema.get(\"title\") || name\n let requiredProperties = schema.get(\"required\")\n\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Markdown = getComponent(\"Markdown\")\n const Model = getComponent(\"Model\")\n const ModelCollapse = getComponent(\"ModelCollapse\")\n\n const JumpToPathSection = () => {\n return \n }\n const collapsedContent = (\n { braceOpen }...{ braceClose }\n {\n isRef ? : \"\"\n }\n )\n\n const anyOf = specSelectors.isOAS3() ? schema.get(\"anyOf\") : null\n const oneOf = specSelectors.isOAS3() ? schema.get(\"oneOf\") : null\n const not = specSelectors.isOAS3() ? schema.get(\"not\") : null\n\n const titleEl = title && \n { isRef && schema.get(\"$$ref\") && { schema.get(\"$$ref\") } }\n { title }\n \n\n return \n \n\n { braceOpen }\n {\n !isRef ? null : \n }\n \n {\n \n {\n !description ? null : \n \n \n \n }\n {\n !(properties && properties.size) ? null : properties.entrySeq().map(\n ([key, value]) => {\n let isDeprecated = isOAS3() && value.get(\"deprecated\")\n let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)\n let propertyStyle = { verticalAlign: \"top\", paddingRight: \"0.2em\" }\n if ( isRequired ) {\n propertyStyle.fontWeight = \"bold\"\n }\n\n return (\n \n \n )\n }).toArray()\n }\n {\n // empty row befor extensions...\n !showExtensions ? null :  \n }\n {\n !showExtensions ? null :\n schema.entrySeq().map(\n ([key, value]) => {\n if(key.slice(0,2) !== \"x-\") {\n return\n }\n\n const normalizedValue = !value ? null : value.toJS ? value.toJS() : value\n\n return (\n \n \n )\n }).toArray()\n }\n {\n !additionalProperties || !additionalProperties.size ? null\n : \n \n \n \n }\n {\n !anyOf ? null\n : \n \n \n \n }\n {\n !oneOf ? null\n : \n \n \n \n }\n {\n !not ? null\n : \n \n \n \n }\n
description:\n \n
\n { key }{ isRequired && * }\n \n \n
\n { key }\n \n { JSON.stringify(normalizedValue) }\n
{ \"< * >:\" }\n \n
{ \"anyOf ->\" }\n {anyOf.map((schema, k) => {\n return
\n })}\n
{ \"oneOf ->\" }\n {oneOf.map((schema, k) => {\n return
\n })}\n
{ \"not ->\" }\n
\n \n
\n
\n }\n
\n { braceClose }\n \n
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/object-model.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { sanitizeUrl } from \"core/utils\"\n\nexport default class OnlineValidatorBadge extends React.Component {\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired\n }\n\n constructor(props, context) {\n super(props, context)\n let { specSelectors, getConfigs } = props\n let { validatorUrl } = getConfigs()\n this.state = {\n url: specSelectors.url(),\n validatorUrl: validatorUrl === undefined ? \"https://online.swagger.io/validator\" : validatorUrl\n }\n }\n\n componentWillReceiveProps(nextProps) {\n let { specSelectors, getConfigs } = nextProps\n let { validatorUrl } = getConfigs()\n\n this.setState({\n url: specSelectors.url(),\n validatorUrl: validatorUrl === undefined ? \"https://online.swagger.io/validator\" : validatorUrl\n })\n }\n\n render() {\n let { getConfigs } = this.props\n let { spec } = getConfigs()\n\n let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl)\n\n if ( typeof spec === \"object\" && Object.keys(spec).length) return null\n\n if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf(\"localhost\") >= 0\n || this.state.url.indexOf(\"127.0.0.1\") >= 0) {\n return null\n }\n\n return (\n \n \n \n )\n }\n}\n\n\nclass ValidatorImage extends React.Component {\n static propTypes = {\n src: PropTypes.string,\n alt: PropTypes.string\n }\n\n constructor(props) {\n super(props)\n this.state = {\n loaded: false,\n error: false\n }\n }\n\n componentDidMount() {\n const img = new Image()\n img.onload = () => {\n this.setState({\n loaded: true\n })\n }\n img.onerror = () => {\n this.setState({\n error: true\n })\n }\n img.src = this.props.src\n }\n\n componentWillReceiveProps(nextProps) {\n if (nextProps.src !== this.props.src) {\n const img = new Image()\n img.onload = () => {\n this.setState({\n loaded: true\n })\n }\n img.onerror = () => {\n this.setState({\n error: true\n })\n }\n img.src = nextProps.src\n }\n }\n\n render() {\n if (this.state.error) {\n return {\"Error\"}\n } else if (!this.state.loaded) {\n return {\"Loading...\"}\n }\n return {this.props.alt}\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/online-validator-badge.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const OperationExtRow = ({ xKey, xVal }) => {\n const xNormalizedValue = !xVal ? null : xVal.toJS ? xVal.toJS() : xVal\n\n return (\n { xKey }\n { JSON.stringify(xNormalizedValue) }\n )\n}\nOperationExtRow.propTypes = {\n xKey: PropTypes.string,\n xVal: PropTypes.any\n}\n\nexport default OperationExtRow\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operation-extension-row.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const OperationExt = ({ extensions, getComponent }) => {\n let OperationExtRow = getComponent(\"OperationExtRow\")\n return (\n
\n
\n

Extensions

\n
\n
\n\n \n \n \n \n \n \n \n \n {\n extensions.entrySeq().map(([k, v]) => )\n }\n \n
FieldValue
\n
\n
\n )\n}\nOperationExt.propTypes = {\n extensions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n}\n\nexport default OperationExt\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operation-extensions.jsx","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { getList } from \"core/utils\"\nimport { getExtensions, sanitizeUrl } from \"core/utils\"\nimport { Iterable, List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class Operation extends PureComponent {\n static propTypes = {\n specPath: ImPropTypes.list.isRequired,\n operation: PropTypes.instanceOf(Iterable).isRequired,\n response: PropTypes.instanceOf(Iterable),\n request: PropTypes.instanceOf(Iterable),\n\n toggleShown: PropTypes.func.isRequired,\n onTryoutClick: PropTypes.func.isRequired,\n onCancelClick: PropTypes.func.isRequired,\n onExecute: PropTypes.func.isRequired,\n\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n authActions: PropTypes.object,\n authSelectors: PropTypes.object,\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n fn: PropTypes.object.isRequired\n }\n\n static defaultProps = {\n operation: null,\n response: null,\n request: null,\n specPath: List()\n }\n\n render() {\n let {\n specPath,\n response,\n request,\n toggleShown,\n onTryoutClick,\n onCancelClick,\n onExecute,\n fn,\n getComponent,\n getConfigs,\n specActions,\n specSelectors,\n authActions,\n authSelectors,\n oas3Actions,\n oas3Selectors\n } = this.props\n let operationProps = this.props.operation\n\n let {\n isShown,\n isAuthorized,\n path,\n method,\n op,\n tag,\n showSummary,\n operationId,\n allowTryItOut,\n displayOperationId,\n displayRequestDuration,\n isDeepLinkingEnabled,\n tryItOutEnabled,\n executeInProgress\n } = operationProps.toJS()\n\n let {\n summary,\n description,\n deprecated,\n externalDocs,\n schemes\n } = op.operation\n\n let operation = operationProps.getIn([\"op\", \"operation\"])\n let security = operationProps.get(\"security\")\n let responses = operation.get(\"responses\")\n let produces = operation.get(\"produces\")\n let parameters = getList(operation, [\"parameters\"])\n let operationScheme = specSelectors.operationScheme(path, method)\n let isShownKey = [\"operations\", tag, operationId]\n let extensions = getExtensions(operation)\n\n const Responses = getComponent(\"responses\")\n const Parameters = getComponent( \"parameters\" )\n const Execute = getComponent( \"execute\" )\n const Clear = getComponent( \"clear\" )\n const AuthorizeOperationBtn = getComponent( \"authorizeOperationBtn\" )\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Collapse = getComponent( \"Collapse\" )\n const Markdown = getComponent( \"Markdown\" )\n const Schemes = getComponent( \"schemes\" )\n const OperationServers = getComponent( \"OperationServers\" )\n const OperationExt = getComponent( \"OperationExt\" )\n const DeepLink = getComponent( \"DeepLink\" )\n\n const { showExtensions } = getConfigs()\n\n // Merge in Live Response\n if(responses && response && response.size > 0) {\n let notDocumented = !responses.get(String(response.get(\"status\"))) && !responses.get(\"default\")\n response = response.set(\"notDocumented\", notDocumented)\n }\n\n let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )\n\n return (\n
\n
\n {/*TODO: convert this into a component, that can be wrapped\n and pulled in with getComponent */}\n {method.toUpperCase()}\n \n \n {/*TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}\n \n\n { !showSummary ? null :\n
\n { summary }\n
\n }\n\n { displayOperationId && operationId ? {operationId} : null }\n\n {\n (!security || !security.count()) ? null :\n {\n const applicableDefinitions = authSelectors.definitionsForRequirements(security)\n authActions.showDefinitions(applicableDefinitions)\n }}\n />\n }\n
\n\n \n
\n { deprecated &&

Warning: Deprecated

}\n { description &&\n
\n
\n \n
\n
\n }\n {\n externalDocs && externalDocs.url ?\n
\n

Find more details

\n
\n \n \n \n { externalDocs.url }\n
\n
: null\n }\n\n \n\n { !tryItOutEnabled ? null :\n \n }\n\n {!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ?
\n \n
: null\n }\n\n
\n { !tryItOutEnabled || !allowTryItOut ? null :\n\n \n }\n\n { (!tryItOutEnabled || !response || !allowTryItOut) ? null :\n \n }\n
\n\n {executeInProgress ?
: null}\n\n { !responses ? null :\n \n }\n\n { !showExtensions || !extensions.size ? null :\n \n }\n
\n
\n
\n )\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operation.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Im from \"immutable\"\nimport { createDeepLinkPath, sanitizeUrl } from \"core/utils\"\n\nconst SWAGGER2_OPERATION_METHODS = [\n \"get\", \"put\", \"post\", \"delete\", \"options\", \"head\", \"patch\"\n]\n\nconst OAS3_OPERATION_METHODS = SWAGGER2_OPERATION_METHODS.concat([\"trace\"])\n\n\nexport default class Operations extends React.Component {\n\n static propTypes = {\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n authSelectors: PropTypes.object.isRequired,\n getConfigs: PropTypes.func.isRequired\n };\n\n render() {\n let {\n specSelectors,\n getComponent,\n layoutSelectors,\n layoutActions,\n getConfigs\n } = this.props\n\n let taggedOps = specSelectors.taggedOperations()\n\n const OperationContainer = getComponent(\"OperationContainer\", true)\n const Collapse = getComponent(\"Collapse\")\n const Markdown = getComponent(\"Markdown\")\n const DeepLink = getComponent(\"DeepLink\")\n\n let {\n docExpansion,\n maxDisplayedTags,\n deepLinking\n } = getConfigs()\n\n const isDeepLinkingEnabled = deepLinking && deepLinking !== \"false\"\n\n let filter = layoutSelectors.currentFilter()\n\n if (filter) {\n if (filter !== true) {\n taggedOps = taggedOps.filter((tagObj, tag) => {\n return tag.indexOf(filter) !== -1\n })\n }\n }\n\n if (maxDisplayedTags && !isNaN(maxDisplayedTags) && maxDisplayedTags >= 0) {\n taggedOps = taggedOps.slice(0, maxDisplayedTags)\n }\n\n return (\n
\n {\n taggedOps.map( (tagObj, tag) => {\n let operations = tagObj.get(\"operations\")\n let tagDescription = tagObj.getIn([\"tagDetails\", \"description\"], null)\n let tagExternalDocsDescription = tagObj.getIn([\"tagDetails\", \"externalDocs\", \"description\"])\n let tagExternalDocsUrl = tagObj.getIn([\"tagDetails\", \"externalDocs\", \"url\"])\n\n let isShownKey = [\"operations-tag\", createDeepLinkPath(tag)]\n let showTag = layoutSelectors.isShown(isShownKey, docExpansion === \"full\" || docExpansion === \"list\")\n\n return (\n
\n\n layoutActions.show(isShownKey, !showTag)}\n className={!tagDescription ? \"opblock-tag no-desc\" : \"opblock-tag\" }\n id={isShownKey.join(\"-\")}>\n \n { !tagDescription ? :\n \n \n \n }\n\n
\n { !tagExternalDocsDescription ? null :\n \n { tagExternalDocsDescription }\n { tagExternalDocsUrl ? \": \" : null }\n { tagExternalDocsUrl ?\n e.stopPropagation()}\n target={\"_blank\"}\n >{tagExternalDocsUrl} : null\n }\n \n }\n
\n\n \n \n\n \n {\n operations.map( op => {\n const path = op.get(\"path\")\n const method = op.get(\"method\")\n const specPath = Im.List([\"paths\", path, method])\n\n\n // FIXME: (someday) this logic should probably be in a selector,\n // but doing so would require further opening up\n // selectors to the plugin system, to allow for dynamic\n // overriding of low-level selectors that other selectors\n // rely on. --KS, 12/17\n const validMethods = specSelectors.isOAS3() ?\n OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS\n\n if(validMethods.indexOf(method) === -1) {\n return null\n }\n\n return \n }).toArray()\n }\n \n
\n )\n }).toArray()\n }\n\n { taggedOps.size < 1 ?

No operations defined in spec!

: null }\n
\n )\n }\n\n}\n\nOperations.propTypes = {\n layoutActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n fn: PropTypes.object.isRequired\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operations.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { Link } from \"core/components/layout-utils\"\n\nexport default class Overview extends React.Component {\n\n constructor(...args) {\n super(...args)\n this.setTagShown = this._setTagShown.bind(this)\n }\n\n _setTagShown(showTagId, shown) {\n this.props.layoutActions.show(showTagId, shown)\n }\n\n showOp(key, shown) {\n let { layoutActions } = this.props\n layoutActions.show(key, shown)\n }\n\n render() {\n let { specSelectors, layoutSelectors, layoutActions, getComponent } = this.props\n let taggedOps = specSelectors.taggedOperations()\n\n const Collapse = getComponent(\"Collapse\")\n\n return (\n
\n

Overview

\n\n {\n taggedOps.map( (tagObj, tag) => {\n let operations = tagObj.get(\"operations\")\n\n let showTagId = [\"overview-tags\", tag]\n let showTag = layoutSelectors.isShown(showTagId, true)\n let toggleShow = ()=> layoutActions.show(showTagId, !showTag)\n\n return (\n
\n\n\n

{showTag ? \"-\" : \"+\"}{tag}

\n\n \n {\n operations.map( op => {\n let { path, method, id } = op.toObject() // toObject is shallow\n let showOpIdPrefix = \"operations\"\n let showOpId = id\n let shown = layoutSelectors.isShown([showOpIdPrefix, showOpId])\n return \n }).toArray()\n }\n \n\n
\n )\n }).toArray()\n }\n\n { taggedOps.size < 1 &&

No operations defined in spec!

}\n
\n )\n }\n\n}\n\nOverview.propTypes = {\n layoutSelectors: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n}\n\nexport class OperationLink extends React.Component {\n\n constructor(props) {\n super(props)\n this.onClick = this._onClick.bind(this)\n }\n\n _onClick() {\n let { showOpId, showOpIdPrefix, onClick, shown } = this.props\n onClick([showOpIdPrefix, showOpId], !shown)\n }\n\n render() {\n let { id, method, shown, href } = this.props\n\n return (\n \n
\n {method.toUpperCase()}\n {id}\n
\n \n )\n }\n\n}\n\nOperationLink.propTypes = {\n href: PropTypes.string,\n onClick: PropTypes.func,\n id: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n shown: PropTypes.bool.isRequired,\n showOpId: PropTypes.string.isRequired,\n showOpIdPrefix: PropTypes.string.isRequired\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/overview.jsx","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { fromJS, List } from \"immutable\"\nimport { getSampleSchema } from \"core/utils\"\n\nconst NOOP = Function.prototype\n\nexport default class ParamBody extends PureComponent {\n\n static propTypes = {\n param: PropTypes.object,\n onChange: PropTypes.func,\n onChangeConsumes: PropTypes.func,\n consumes: PropTypes.object,\n consumesValue: PropTypes.string,\n fn: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n isExecute: PropTypes.bool,\n specSelectors: PropTypes.object.isRequired,\n pathMethod: PropTypes.array.isRequired\n };\n\n static defaultProp = {\n consumes: fromJS([\"application/json\"]),\n param: fromJS({}),\n onChange: NOOP,\n onChangeConsumes: NOOP,\n };\n\n constructor(props, context) {\n super(props, context)\n\n this.state = {\n isEditBox: false,\n value: \"\"\n }\n\n }\n\n componentDidMount() {\n this.updateValues.call(this, this.props)\n }\n\n componentWillReceiveProps(nextProps) {\n this.updateValues.call(this, nextProps)\n }\n\n updateValues = (props) => {\n let { specSelectors, pathMethod, param, isExecute, consumesValue=\"\" } = props\n let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get(\"name\"), param.get(\"in\")) : fromJS({})\n let isXml = /xml/i.test(consumesValue)\n let isJson = /json/i.test(consumesValue)\n let paramValue = isXml ? parameter.get(\"value_xml\") : parameter.get(\"value\")\n\n if ( paramValue !== undefined ) {\n let val = !paramValue && isJson ? \"{}\" : paramValue\n this.setState({ value: val })\n this.onChange(val, {isXml: isXml, isEditBox: isExecute})\n } else {\n if (isXml) {\n this.onChange(this.sample(\"xml\"), {isXml: isXml, isEditBox: isExecute})\n } else {\n this.onChange(this.sample(), {isEditBox: isExecute})\n }\n }\n }\n\n sample = (xml) => {\n let { param, fn:{inferSchema} } = this.props\n let schema = inferSchema(param.toJS())\n\n return getSampleSchema(schema, xml, {\n includeWriteOnly: true\n })\n }\n\n onChange = (value, { isEditBox, isXml }) => {\n this.setState({value, isEditBox})\n this._onChange(value, isXml)\n }\n\n _onChange = (val, isXml) => { (this.props.onChange || NOOP)(this.props.param, val, isXml) }\n\n handleOnChange = e => {\n const {consumesValue} = this.props\n const isJson = /json/i.test(consumesValue)\n const isXml = /xml/i.test(consumesValue)\n const inputValue = isJson ? e.target.value.trim() : e.target.value\n this.onChange(inputValue, {isXml})\n }\n\n toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))\n\n render() {\n let {\n onChangeConsumes,\n param,\n isExecute,\n specSelectors,\n pathMethod,\n\n getComponent,\n } = this.props\n\n const Button = getComponent(\"Button\")\n const TextArea = getComponent(\"TextArea\")\n const HighlightCode = getComponent(\"highlightCode\")\n const ContentType = getComponent(\"contentType\")\n // for domains where specSelectors not passed\n let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get(\"name\"), param.get(\"in\")) : param\n let errors = parameter.get(\"errors\", List())\n let consumesValue = specSelectors.contentTypeValues(pathMethod).get(\"requestContentType\")\n let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes\n\n let { value, isEditBox } = this.state\n\n return (\n
\n {\n isEditBox && isExecute\n ? \n
\n
\n )\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/curl.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const DeepLink = ({ enabled, path, text }) => {\n return (\n e.preventDefault() : null}\n href={enabled ? `#/${path}` : null}>\n {text}\n \n )\n}\nDeepLink.propTypes = {\n enabled: PropTypes.bool,\n isShown: PropTypes.bool,\n path: PropTypes.string,\n text: PropTypes.string\n}\n\nexport default DeepLink\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/deep-link.jsx","import React from \"react\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst EnumModel = ({ value, getComponent }) => {\n let ModelCollapse = getComponent(\"ModelCollapse\")\n let collapsedContent = Array [ { value.count() } ]\n return \n Enum:
\n \n [ { value.join(\", \") } ]\n \n
\n}\nEnumModel.propTypes = {\n value: ImPropTypes.iterable,\n getComponent: ImPropTypes.func\n}\n\nexport default EnumModel\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/enum-model.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List } from \"immutable\"\nimport { Collapse } from \"react-collapse\"\n\nexport default class Errors extends React.Component {\n\n static propTypes = {\n editorActions: PropTypes.object,\n errSelectors: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired\n }\n\n render() {\n let { editorActions, errSelectors, layoutSelectors, layoutActions } = this.props\n\n if(editorActions && editorActions.jumpToLine) {\n var jumpToLine = editorActions.jumpToLine\n }\n\n let errors = errSelectors.allErrors()\n\n // all thrown errors, plus error-level everything else\n let allErrorsToDisplay = errors.filter(err => err.get(\"type\") === \"thrown\" ? true :err.get(\"level\") === \"error\")\n\n if(!allErrorsToDisplay || allErrorsToDisplay.count() < 1) {\n return null\n }\n\n let isVisible = layoutSelectors.isShown([\"errorPane\"], true)\n let toggleVisibility = () => layoutActions.show([\"errorPane\"], !isVisible)\n\n let sortedJSErrors = allErrorsToDisplay.sortBy(err => err.get(\"line\"))\n\n return (\n
\n        
\n

Errors

\n \n
\n \n
\n { sortedJSErrors.map((err, i) => {\n let type = err.get(\"type\")\n if(type === \"thrown\" || type === \"auth\") {\n return \n }\n if(type === \"spec\") {\n return \n }\n }) }\n
\n
\n
\n )\n }\n}\n\nconst ThrownErrorItem = ( { error, jumpToLine } ) => {\n if(!error) {\n return null\n }\n let errorLine = error.get(\"line\")\n\n return (\n
\n { !error ? null :\n
\n

{ (error.get(\"source\") && error.get(\"level\")) ?\n toTitleCase(error.get(\"source\")) + \" \" + error.get(\"level\") : \"\" }\n { error.get(\"path\") ? at {error.get(\"path\")}: null }

\n \n { error.get(\"message\") }\n \n
\n { errorLine && jumpToLine ? Jump to line { errorLine } : null }\n
\n
\n }\n
\n )\n }\n\nconst SpecErrorItem = ( { error, jumpToLine } ) => {\n let locationMessage = null\n\n if(error.get(\"path\")) {\n if(List.isList(error.get(\"path\"))) {\n locationMessage = at { error.get(\"path\").join(\".\") }\n } else {\n locationMessage = at { error.get(\"path\") }\n }\n } else if(error.get(\"line\") && !jumpToLine) {\n locationMessage = on line { error.get(\"line\") }\n }\n\n return (\n
\n { !error ? null :\n
\n

{ toTitleCase(error.get(\"source\")) + \" \" + error.get(\"level\") } { locationMessage }

\n { error.get(\"message\") }\n
\n { jumpToLine ? (\n Jump to line { error.get(\"line\") }\n ) : null }\n
\n
\n }\n
\n )\n }\n\nfunction toTitleCase(str) {\n return (str || \"\")\n .split(\" \")\n .map(substr => substr[0].toUpperCase() + substr.slice(1))\n .join(\" \")\n}\n\nThrownErrorItem.propTypes = {\n error: PropTypes.object.isRequired,\n jumpToLine: PropTypes.func\n}\n\nThrownErrorItem.defaultProps = {\n jumpToLine: null\n}\n\nSpecErrorItem.propTypes = {\n error: PropTypes.object.isRequired,\n jumpToLine: PropTypes.func\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/errors.jsx","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class Execute extends Component {\n\n static propTypes = {\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n operation: PropTypes.object.isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n onExecute: PropTypes.func\n }\n\n onClick=()=>{\n let { specSelectors, specActions, operation, path, method } = this.props\n\n specActions.validateParams( [path, method] )\n\n if ( specSelectors.validateBeforeExecute([path, method]) ) {\n if(this.props.onExecute) {\n this.props.onExecute()\n }\n specActions.execute( { operation, path, method } )\n }\n }\n\n onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)\n\n render(){\n return (\n \n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/execute.jsx","import React from \"react\"\n\nexport default class Footer extends React.Component {\n render() {\n return (\n
\n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/footer.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Im from \"immutable\"\n\nconst propStyle = { color: \"#999\", fontStyle: \"italic\" }\n\nexport default class Headers extends React.Component {\n\n static propTypes = {\n headers: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n };\n\n render() {\n\n let { headers, getComponent } = this.props\n const Property = getComponent(\"Property\")\n\n if ( !headers || !headers.size )\n return null\n\n return (\n
\n

Headers:

\n \n \n \n \n \n \n \n \n \n {\n headers.entrySeq().map( ([ key, header ]) => {\n if(!Im.Map.isMap(header)) {\n return null\n }\n const type = header.getIn([\"schema\"]) ? header.getIn([\"schema\", \"type\"]) : header.getIn([\"type\"])\n const schemaExample = header.getIn([\"schema\", \"example\"])\n\n return (\n \n \n \n )\n }).toArray()\n }\n \n
NameDescriptionType
{ key }{ header.get( \"description\" ) }{ type } { schemaExample ? : null }
\n
\n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/headers.jsx","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { highlight } from \"core/utils\"\n\nexport default class HighlightCode extends Component {\n static propTypes = {\n value: PropTypes.string.isRequired,\n className: PropTypes.string\n }\n\n componentDidMount() {\n highlight(this.el)\n }\n\n componentDidUpdate() {\n highlight(this.el)\n }\n\n initializeComponent = (c) => {\n this.el = c\n }\n\n render () {\n let { value, className } = this.props\n className = className || \"\"\n\n return
{ value }
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/highlight-code.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { fromJS } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { sanitizeUrl } from \"core/utils\"\n\n\nclass Path extends React.Component {\n static propTypes = {\n host: PropTypes.string,\n basePath: PropTypes.string\n }\n\n render() {\n let { host, basePath } = this.props\n\n return (\n
\n        [ Base URL: {host}{basePath} ]\n      
\n )\n }\n}\n\n\nclass Contact extends React.Component {\n static propTypes = {\n data: PropTypes.object\n }\n\n render(){\n let { data } = this.props\n let name = data.get(\"name\") || \"the developer\"\n let url = data.get(\"url\")\n let email = data.get(\"email\")\n\n return (\n \n )\n }\n}\n\nclass License extends React.Component {\n static propTypes = {\n license: PropTypes.object\n }\n\n render(){\n let { license } = this.props\n let name = license.get(\"name\") || \"License\"\n let url = license.get(\"url\")\n\n return (\n
\n {\n url ? { name }\n : { name }\n }\n
\n )\n }\n}\n\nexport default class Info extends React.Component {\n static propTypes = {\n info: PropTypes.object,\n url: PropTypes.string,\n host: PropTypes.string,\n basePath: PropTypes.string,\n externalDocs: ImPropTypes.map,\n getComponent: PropTypes.func.isRequired,\n }\n\n render() {\n let { info, url, host, basePath, getComponent, externalDocs } = this.props\n let version = info.get(\"version\")\n let description = info.get(\"description\")\n let title = info.get(\"title\")\n let termsOfService = info.get(\"termsOfService\")\n let contact = info.get(\"contact\")\n let license = info.get(\"license\")\n const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()\n\n const Markdown = getComponent(\"Markdown\")\n const VersionStamp = getComponent(\"VersionStamp\")\n\n return (\n
\n
\n

{ title }\n { version && }\n

\n { host || basePath ? : null }\n { url && { url } }\n
\n\n
\n \n
\n\n {\n termsOfService && \n }\n\n { contact && contact.size ? : null }\n { license && license.size ? : null }\n { externalDocsUrl ?\n {externalDocsDescription || externalDocsUrl}\n : null }\n\n
\n )\n }\n\n}\n\nInfo.propTypes = {\n title: PropTypes.any,\n description: PropTypes.any,\n version: PropTypes.any,\n url: PropTypes.string\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/info.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class BaseLayout extends React.Component {\n\n static propTypes = {\n errSelectors: PropTypes.object.isRequired,\n errActions: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n }\n\n onFilterChange =(e) => {\n let {target: {value}} = e\n this.props.layoutActions.updateFilter(value)\n }\n\n render() {\n let {\n specSelectors,\n specActions,\n getComponent,\n layoutSelectors,\n oas3Selectors,\n oas3Actions\n } = this.props\n\n let info = specSelectors.info()\n let url = specSelectors.url()\n let basePath = specSelectors.basePath()\n let host = specSelectors.host()\n let securityDefinitions = specSelectors.securityDefinitions()\n let externalDocs = specSelectors.externalDocs()\n let schemes = specSelectors.schemes()\n let servers = specSelectors.servers()\n\n let Info = getComponent(\"info\")\n let Operations = getComponent(\"operations\", true)\n let Models = getComponent(\"Models\", true)\n let AuthorizeBtn = getComponent(\"authorizeBtn\", true)\n let Row = getComponent(\"Row\")\n let Col = getComponent(\"Col\")\n let Servers = getComponent(\"Servers\")\n let Errors = getComponent(\"errors\", true)\n\n let isLoading = specSelectors.loadingStatus() === \"loading\"\n let isFailed = specSelectors.loadingStatus() === \"failed\"\n let filter = layoutSelectors.currentFilter()\n\n let inputStyle = {}\n if(isFailed) inputStyle.color = \"red\"\n if(isLoading) inputStyle.color = \"#aaa\"\n\n const Schemes = getComponent(\"schemes\")\n\n const isSpecEmpty = !specSelectors.specStr()\n\n if(isSpecEmpty) {\n let loadingMessage\n if(isLoading) {\n loadingMessage =
\n } else {\n loadingMessage =

No API definition provided.

\n }\n\n return
\n
\n {loadingMessage}\n
\n
\n }\n\n return (\n\n
\n
\n \n \n \n { info.count() ? (\n \n ) : null }\n \n \n { schemes && schemes.size || securityDefinitions ? (\n
\n \n { schemes && schemes.size ? (\n \n ) : null }\n\n { securityDefinitions ? (\n \n ) : null }\n \n
\n ) : null }\n\n { servers && servers.size ? (\n
\n \n Server\n \n \n
\n\n ) : null}\n\n {\n filter === null || filter === false ? null :\n
\n \n \n \n
\n }\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/layouts/base.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { Iterable } from \"immutable\"\n\nconst Headers = ( { headers } )=>{\n return (\n
\n
Response headers
\n
{headers}
\n
)\n}\nHeaders.propTypes = {\n headers: PropTypes.array.isRequired\n}\n\nconst Duration = ( { duration } ) => {\n return (\n
\n
Request duration
\n
{duration} ms
\n
\n )\n}\nDuration.propTypes = {\n duration: PropTypes.number.isRequired\n}\n\n\nexport default class LiveResponse extends React.Component {\n static propTypes = {\n response: PropTypes.instanceOf(Iterable).isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n displayRequestDuration: PropTypes.bool.isRequired,\n specSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired\n }\n\n shouldComponentUpdate(nextProps) {\n // BUG: props.response is always coming back as a new Immutable instance\n // same issue as responses.jsx (tryItOutResponse)\n return this.props.response !== nextProps.response\n || this.props.path !== nextProps.path\n || this.props.method !== nextProps.method\n || this.props.displayRequestDuration !== nextProps.displayRequestDuration\n }\n\n render() {\n const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props\n const { showMutatedRequest } = getConfigs()\n\n const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)\n const status = response.get(\"status\")\n const url = response.get(\"url\")\n const headers = response.get(\"headers\").toJS()\n const notDocumented = response.get(\"notDocumented\")\n const isError = response.get(\"error\")\n const body = response.get(\"text\")\n const duration = response.get(\"duration\")\n const headersKeys = Object.keys(headers)\n const contentType = headers[\"content-type\"]\n\n const Curl = getComponent(\"curl\")\n const ResponseBody = getComponent(\"responseBody\")\n const returnObject = headersKeys.map(key => {\n return {key}: {headers[key]} \n })\n const hasHeaders = returnObject.length !== 0\n\n return (\n
\n { curlRequest && }\n { url &&
\n

Request URL

\n
\n
{url}
\n
\n
\n }\n

Server response

\n \n \n \n \n \n \n \n \n \n \n \n \n \n
CodeDetails
\n { status }\n {\n notDocumented ?
\n Undocumented \n
\n : null\n }\n
\n {\n isError ? \n {`${response.get(\"name\")}: ${response.get(\"message\")}`}\n \n : null\n }\n {\n body ? \n : null\n }\n {\n hasHeaders ? : null\n }\n {\n displayRequestDuration && duration ? : null\n }\n
\n
\n )\n }\n\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n response: ImPropTypes.map\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/live-response.jsx","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class ModelCollapse extends Component {\n static propTypes = {\n collapsedContent: PropTypes.any,\n expanded: PropTypes.bool,\n children: PropTypes.any,\n title: PropTypes.element,\n modelName: PropTypes.string,\n onToggle: PropTypes.func\n }\n\n static defaultProps = {\n collapsedContent: \"{...}\",\n expanded: false,\n title: null,\n onToggle: () => {}\n }\n\n constructor(props, context) {\n super(props, context)\n\n let { expanded, collapsedContent } = this.props\n\n this.state = {\n expanded : expanded,\n collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent\n }\n }\n\n componentWillReceiveProps(nextProps){\n\n if(this.props.expanded!= nextProps.expanded){\n this.setState({expanded: nextProps.expanded})\n }\n\n }\n\n toggleCollapsed=()=>{\n\n\n if(this.props.onToggle){\n this.props.onToggle(this.props.modelName,!this.state.expanded)\n }\n\n this.setState({\n expanded: !this.state.expanded\n })\n }\n\n render () {\n const {title} = this.props\n return (\n \n { title && {title} }\n \n \n \n { this.state.expanded ? this.props.children :this.state.collapsedContent }\n \n )\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/model-collapse.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class ModelExample extends React.Component {\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n schema: PropTypes.object.isRequired,\n example: PropTypes.any.isRequired,\n isExecute: PropTypes.bool,\n getConfigs: PropTypes.func.isRequired,\n specPath: ImPropTypes.list.isRequired,\n }\n\n constructor(props, context) {\n super(props, context)\n let { getConfigs } = this.props\n let { defaultModelRendering } = getConfigs()\n if (defaultModelRendering !== \"example\" && defaultModelRendering !== \"model\") {\n defaultModelRendering = \"example\"\n }\n this.state = {\n activeTab: defaultModelRendering\n }\n }\n\n activeTab =( e ) => {\n let { target : { dataset : { name } } } = e\n\n this.setState({\n activeTab: name\n })\n }\n\n render() {\n let { getComponent, specSelectors, schema, example, isExecute, getConfigs, specPath } = this.props\n let { defaultModelExpandDepth } = getConfigs()\n const ModelWrapper = getComponent(\"ModelWrapper\")\n\n return
\n \n
\n {\n (isExecute || this.state.activeTab === \"example\") && example\n }\n {\n !isExecute && this.state.activeTab === \"model\" && \n\n\n }\n
\n
\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/model-example.jsx","import React, { Component, } from \"react\"\nimport PropTypes from \"prop-types\"\n//import layoutActions from \"actions/layout\"\n\n\nexport default class ModelWrapper extends Component {\n\n\n static propTypes = {\n schema: PropTypes.object.isRequired,\n name: PropTypes.string,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n expandDepth: PropTypes.number,\n layoutActions: PropTypes.object,\n layoutSelectors: PropTypes.object.isRequired\n }\n\n onToggle = (name,isShown) => {\n // If this prop is present, we'll have deepLinking for it\n if(this.props.layoutActions) {\n this.props.layoutActions.show([\"models\", name],isShown)\n }\n }\n\n render(){\n let { getComponent, getConfigs } = this.props\n const Model = getComponent(\"Model\")\n\n let expanded\n if(this.props.layoutSelectors) {\n // If this is prop is present, we'll have deepLinking for it\n expanded = this.props.layoutSelectors.isShown([\"models\",this.props.name])\n }\n\n return
\n \n
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/model-wrapper.jsx","import React, { Component } from \"react\"\nimport Im from \"immutable\"\nimport PropTypes from \"prop-types\"\n\nexport default class Models extends Component {\n static propTypes = {\n getComponent: PropTypes.func,\n specSelectors: PropTypes.object,\n layoutSelectors: PropTypes.object,\n layoutActions: PropTypes.object,\n getConfigs: PropTypes.func.isRequired\n }\n\n render(){\n let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props\n let definitions = specSelectors.definitions()\n let { docExpansion, defaultModelsExpandDepth } = getConfigs()\n if (!definitions.size || defaultModelsExpandDepth < 0) return null\n\n let showModels = layoutSelectors.isShown(\"models\", defaultModelsExpandDepth > 0 && docExpansion !== \"none\")\n const specPathBase = specSelectors.isOAS3() ? [\"components\", \"schemas\"] : [\"definitions\"]\n\n const ModelWrapper = getComponent(\"ModelWrapper\")\n const Collapse = getComponent(\"Collapse\")\n\n return
\n

layoutActions.show(\"models\", !showModels)}>\n Models\n \n \n \n

\n \n {\n definitions.entrySeq().map( ( [ name, model ])=>{\n\n return
\n \n
\n }).toArray()\n }\n
\n
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/models.jsx","import React, { Component, } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst braceOpen = \"{\"\nconst braceClose = \"}\"\n\nexport default class ObjectModel extends Component {\n static propTypes = {\n schema: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n expanded: PropTypes.bool,\n onToggle: PropTypes.func,\n specSelectors: PropTypes.object.isRequired,\n name: PropTypes.string,\n isRef: PropTypes.bool,\n expandDepth: PropTypes.number,\n depth: PropTypes.number,\n specPath: ImPropTypes.list.isRequired\n }\n\n render(){\n let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props\n let { specSelectors,expandDepth } = otherProps\n const { isOAS3 } = specSelectors\n\n if(!schema) {\n return null\n }\n\n const { showExtensions } = getConfigs()\n\n let description = schema.get(\"description\")\n let properties = schema.get(\"properties\")\n let additionalProperties = schema.get(\"additionalProperties\")\n let title = schema.get(\"title\") || name\n let requiredProperties = schema.get(\"required\")\n\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Markdown = getComponent(\"Markdown\")\n const Model = getComponent(\"Model\")\n const ModelCollapse = getComponent(\"ModelCollapse\")\n\n const JumpToPathSection = () => {\n return \n }\n const collapsedContent = (\n { braceOpen }...{ braceClose }\n {\n isRef ? : \"\"\n }\n )\n\n const anyOf = specSelectors.isOAS3() ? schema.get(\"anyOf\") : null\n const oneOf = specSelectors.isOAS3() ? schema.get(\"oneOf\") : null\n const not = specSelectors.isOAS3() ? schema.get(\"not\") : null\n\n const titleEl = title && \n { isRef && schema.get(\"$$ref\") && { schema.get(\"$$ref\") } }\n { title }\n \n\n return \n \n\n { braceOpen }\n {\n !isRef ? null : \n }\n \n {\n \n {\n !description ? null : \n \n \n \n }\n {\n !(properties && properties.size) ? null : properties.entrySeq().map(\n ([key, value]) => {\n let isDeprecated = isOAS3() && value.get(\"deprecated\")\n let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)\n let propertyStyle = { verticalAlign: \"top\", paddingRight: \"0.2em\" }\n if ( isRequired ) {\n propertyStyle.fontWeight = \"bold\"\n }\n\n return (\n \n \n )\n }).toArray()\n }\n {\n // empty row befor extensions...\n !showExtensions ? null :  \n }\n {\n !showExtensions ? null :\n schema.entrySeq().map(\n ([key, value]) => {\n if(key.slice(0,2) !== \"x-\") {\n return\n }\n\n const normalizedValue = !value ? null : value.toJS ? value.toJS() : value\n\n return (\n \n \n )\n }).toArray()\n }\n {\n !additionalProperties || !additionalProperties.size ? null\n : \n \n \n \n }\n {\n !anyOf ? null\n : \n \n \n \n }\n {\n !oneOf ? null\n : \n \n \n \n }\n {\n !not ? null\n : \n \n \n \n }\n
description:\n \n
\n { key }{ isRequired && * }\n \n \n
\n { key }\n \n { JSON.stringify(normalizedValue) }\n
{ \"< * >:\" }\n \n
{ \"anyOf ->\" }\n {anyOf.map((schema, k) => {\n return
\n })}\n
{ \"oneOf ->\" }\n {oneOf.map((schema, k) => {\n return
\n })}\n
{ \"not ->\" }\n
\n \n
\n
\n }\n
\n { braceClose }\n \n
\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/object-model.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { sanitizeUrl } from \"core/utils\"\n\nexport default class OnlineValidatorBadge extends React.Component {\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired\n }\n\n constructor(props, context) {\n super(props, context)\n let { specSelectors, getConfigs } = props\n let { validatorUrl } = getConfigs()\n this.state = {\n url: specSelectors.url(),\n validatorUrl: validatorUrl === undefined ? \"https://online.swagger.io/validator\" : validatorUrl\n }\n }\n\n componentWillReceiveProps(nextProps) {\n let { specSelectors, getConfigs } = nextProps\n let { validatorUrl } = getConfigs()\n\n this.setState({\n url: specSelectors.url(),\n validatorUrl: validatorUrl === undefined ? \"https://online.swagger.io/validator\" : validatorUrl\n })\n }\n\n render() {\n let { getConfigs } = this.props\n let { spec } = getConfigs()\n\n let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl)\n\n if ( typeof spec === \"object\" && Object.keys(spec).length) return null\n\n if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf(\"localhost\") >= 0\n || this.state.url.indexOf(\"127.0.0.1\") >= 0) {\n return null\n }\n\n return (\n \n \n \n )\n }\n}\n\n\nclass ValidatorImage extends React.Component {\n static propTypes = {\n src: PropTypes.string,\n alt: PropTypes.string\n }\n\n constructor(props) {\n super(props)\n this.state = {\n loaded: false,\n error: false\n }\n }\n\n componentDidMount() {\n const img = new Image()\n img.onload = () => {\n this.setState({\n loaded: true\n })\n }\n img.onerror = () => {\n this.setState({\n error: true\n })\n }\n img.src = this.props.src\n }\n\n componentWillReceiveProps(nextProps) {\n if (nextProps.src !== this.props.src) {\n const img = new Image()\n img.onload = () => {\n this.setState({\n loaded: true\n })\n }\n img.onerror = () => {\n this.setState({\n error: true\n })\n }\n img.src = nextProps.src\n }\n }\n\n render() {\n if (this.state.error) {\n return {\"Error\"}\n } else if (!this.state.loaded) {\n return {\"Loading...\"}\n }\n return {this.props.alt}\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/online-validator-badge.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const OperationExtRow = ({ xKey, xVal }) => {\n const xNormalizedValue = !xVal ? null : xVal.toJS ? xVal.toJS() : xVal\n\n return (\n { xKey }\n { JSON.stringify(xNormalizedValue) }\n )\n}\nOperationExtRow.propTypes = {\n xKey: PropTypes.string,\n xVal: PropTypes.any\n}\n\nexport default OperationExtRow\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operation-extension-row.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const OperationExt = ({ extensions, getComponent }) => {\n let OperationExtRow = getComponent(\"OperationExtRow\")\n return (\n
\n
\n

Extensions

\n
\n
\n\n \n \n \n \n \n \n \n \n {\n extensions.entrySeq().map(([k, v]) => )\n }\n \n
FieldValue
\n
\n
\n )\n}\nOperationExt.propTypes = {\n extensions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n}\n\nexport default OperationExt\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operation-extensions.jsx","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { getList } from \"core/utils\"\nimport { getExtensions, sanitizeUrl } from \"core/utils\"\nimport { Iterable, List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class Operation extends PureComponent {\n static propTypes = {\n specPath: ImPropTypes.list.isRequired,\n operation: PropTypes.instanceOf(Iterable).isRequired,\n response: PropTypes.instanceOf(Iterable),\n request: PropTypes.instanceOf(Iterable),\n\n toggleShown: PropTypes.func.isRequired,\n onTryoutClick: PropTypes.func.isRequired,\n onCancelClick: PropTypes.func.isRequired,\n onExecute: PropTypes.func.isRequired,\n\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n authActions: PropTypes.object,\n authSelectors: PropTypes.object,\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n fn: PropTypes.object.isRequired\n }\n\n static defaultProps = {\n operation: null,\n response: null,\n request: null,\n specPath: List()\n }\n\n render() {\n let {\n specPath,\n response,\n request,\n toggleShown,\n onTryoutClick,\n onCancelClick,\n onExecute,\n fn,\n getComponent,\n getConfigs,\n specActions,\n specSelectors,\n authActions,\n authSelectors,\n oas3Actions,\n oas3Selectors\n } = this.props\n let operationProps = this.props.operation\n\n let {\n isShown,\n isAuthorized,\n path,\n method,\n op,\n tag,\n showSummary,\n operationId,\n allowTryItOut,\n displayOperationId,\n displayRequestDuration,\n isDeepLinkingEnabled,\n tryItOutEnabled,\n executeInProgress\n } = operationProps.toJS()\n\n let {\n summary,\n description,\n deprecated,\n externalDocs,\n schemes\n } = op.operation\n\n let operation = operationProps.getIn([\"op\", \"operation\"])\n let security = operationProps.get(\"security\")\n let responses = operation.get(\"responses\")\n let produces = operation.get(\"produces\")\n let parameters = getList(operation, [\"parameters\"])\n let operationScheme = specSelectors.operationScheme(path, method)\n let isShownKey = [\"operations\", tag, operationId]\n let extensions = getExtensions(operation)\n\n const Responses = getComponent(\"responses\")\n const Parameters = getComponent( \"parameters\" )\n const Execute = getComponent( \"execute\" )\n const Clear = getComponent( \"clear\" )\n const AuthorizeOperationBtn = getComponent( \"authorizeOperationBtn\" )\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Collapse = getComponent( \"Collapse\" )\n const Markdown = getComponent( \"Markdown\" )\n const Schemes = getComponent( \"schemes\" )\n const OperationServers = getComponent( \"OperationServers\" )\n const OperationExt = getComponent( \"OperationExt\" )\n const DeepLink = getComponent( \"DeepLink\" )\n\n const { showExtensions } = getConfigs()\n\n // Merge in Live Response\n if(responses && response && response.size > 0) {\n let notDocumented = !responses.get(String(response.get(\"status\"))) && !responses.get(\"default\")\n response = response.set(\"notDocumented\", notDocumented)\n }\n\n let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )\n\n return (\n
\n
\n {/*TODO: convert this into a component, that can be wrapped\n and pulled in with getComponent */}\n {method.toUpperCase()}\n \n \n {/*TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}\n \n\n { !showSummary ? null :\n
\n { summary }\n
\n }\n\n { displayOperationId && operationId ? {operationId} : null }\n\n {\n (!security || !security.count()) ? null :\n {\n const applicableDefinitions = authSelectors.definitionsForRequirements(security)\n authActions.showDefinitions(applicableDefinitions)\n }}\n />\n }\n
\n\n \n
\n { deprecated &&

Warning: Deprecated

}\n { description &&\n
\n
\n \n
\n
\n }\n {\n externalDocs && externalDocs.url ?\n
\n

Find more details

\n
\n \n \n \n { externalDocs.url }\n
\n
: null\n }\n\n \n\n { !tryItOutEnabled ? null :\n \n }\n\n {!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ?
\n \n
: null\n }\n\n
\n { !tryItOutEnabled || !allowTryItOut ? null :\n\n \n }\n\n { (!tryItOutEnabled || !response || !allowTryItOut) ? null :\n \n }\n
\n\n {executeInProgress ?
: null}\n\n { !responses ? null :\n \n }\n\n { !showExtensions || !extensions.size ? null :\n \n }\n
\n
\n
\n )\n }\n\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operation.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Im from \"immutable\"\nimport { createDeepLinkPath, sanitizeUrl } from \"core/utils\"\n\nconst SWAGGER2_OPERATION_METHODS = [\n \"get\", \"put\", \"post\", \"delete\", \"options\", \"head\", \"patch\"\n]\n\nconst OAS3_OPERATION_METHODS = SWAGGER2_OPERATION_METHODS.concat([\"trace\"])\n\n\nexport default class Operations extends React.Component {\n\n static propTypes = {\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n authSelectors: PropTypes.object.isRequired,\n getConfigs: PropTypes.func.isRequired\n };\n\n render() {\n let {\n specSelectors,\n getComponent,\n layoutSelectors,\n layoutActions,\n getConfigs\n } = this.props\n\n let taggedOps = specSelectors.taggedOperations()\n\n const OperationContainer = getComponent(\"OperationContainer\", true)\n const Collapse = getComponent(\"Collapse\")\n const Markdown = getComponent(\"Markdown\")\n const DeepLink = getComponent(\"DeepLink\")\n\n let {\n docExpansion,\n maxDisplayedTags,\n deepLinking\n } = getConfigs()\n\n const isDeepLinkingEnabled = deepLinking && deepLinking !== \"false\"\n\n let filter = layoutSelectors.currentFilter()\n\n if (filter) {\n if (filter !== true) {\n taggedOps = taggedOps.filter((tagObj, tag) => {\n return tag.indexOf(filter) !== -1\n })\n }\n }\n\n if (maxDisplayedTags && !isNaN(maxDisplayedTags) && maxDisplayedTags >= 0) {\n taggedOps = taggedOps.slice(0, maxDisplayedTags)\n }\n\n return (\n
\n {\n taggedOps.map( (tagObj, tag) => {\n let operations = tagObj.get(\"operations\")\n let tagDescription = tagObj.getIn([\"tagDetails\", \"description\"], null)\n let tagExternalDocsDescription = tagObj.getIn([\"tagDetails\", \"externalDocs\", \"description\"])\n let tagExternalDocsUrl = tagObj.getIn([\"tagDetails\", \"externalDocs\", \"url\"])\n\n let isShownKey = [\"operations-tag\", createDeepLinkPath(tag)]\n let showTag = layoutSelectors.isShown(isShownKey, docExpansion === \"full\" || docExpansion === \"list\")\n\n return (\n
\n\n layoutActions.show(isShownKey, !showTag)}\n className={!tagDescription ? \"opblock-tag no-desc\" : \"opblock-tag\" }\n id={isShownKey.join(\"-\")}>\n \n { !tagDescription ? :\n \n \n \n }\n\n
\n { !tagExternalDocsDescription ? null :\n \n { tagExternalDocsDescription }\n { tagExternalDocsUrl ? \": \" : null }\n { tagExternalDocsUrl ?\n e.stopPropagation()}\n target={\"_blank\"}\n >{tagExternalDocsUrl} : null\n }\n \n }\n
\n\n \n \n\n \n {\n operations.map( op => {\n const path = op.get(\"path\")\n const method = op.get(\"method\")\n const specPath = Im.List([\"paths\", path, method])\n\n\n // FIXME: (someday) this logic should probably be in a selector,\n // but doing so would require further opening up\n // selectors to the plugin system, to allow for dynamic\n // overriding of low-level selectors that other selectors\n // rely on. --KS, 12/17\n const validMethods = specSelectors.isOAS3() ?\n OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS\n\n if(validMethods.indexOf(method) === -1) {\n return null\n }\n\n return \n }).toArray()\n }\n \n
\n )\n }).toArray()\n }\n\n { taggedOps.size < 1 ?

No operations defined in spec!

: null }\n
\n )\n }\n\n}\n\nOperations.propTypes = {\n layoutActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n fn: PropTypes.object.isRequired\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/operations.jsx","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { Link } from \"core/components/layout-utils\"\n\nexport default class Overview extends React.Component {\n\n constructor(...args) {\n super(...args)\n this.setTagShown = this._setTagShown.bind(this)\n }\n\n _setTagShown(showTagId, shown) {\n this.props.layoutActions.show(showTagId, shown)\n }\n\n showOp(key, shown) {\n let { layoutActions } = this.props\n layoutActions.show(key, shown)\n }\n\n render() {\n let { specSelectors, layoutSelectors, layoutActions, getComponent } = this.props\n let taggedOps = specSelectors.taggedOperations()\n\n const Collapse = getComponent(\"Collapse\")\n\n return (\n
\n

Overview

\n\n {\n taggedOps.map( (tagObj, tag) => {\n let operations = tagObj.get(\"operations\")\n\n let showTagId = [\"overview-tags\", tag]\n let showTag = layoutSelectors.isShown(showTagId, true)\n let toggleShow = ()=> layoutActions.show(showTagId, !showTag)\n\n return (\n
\n\n\n

{showTag ? \"-\" : \"+\"}{tag}

\n\n \n {\n operations.map( op => {\n let { path, method, id } = op.toObject() // toObject is shallow\n let showOpIdPrefix = \"operations\"\n let showOpId = id\n let shown = layoutSelectors.isShown([showOpIdPrefix, showOpId])\n return \n }).toArray()\n }\n \n\n
\n )\n }).toArray()\n }\n\n { taggedOps.size < 1 &&

No operations defined in spec!

}\n
\n )\n }\n\n}\n\nOverview.propTypes = {\n layoutSelectors: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n}\n\nexport class OperationLink extends React.Component {\n\n constructor(props) {\n super(props)\n this.onClick = this._onClick.bind(this)\n }\n\n _onClick() {\n let { showOpId, showOpIdPrefix, onClick, shown } = this.props\n onClick([showOpIdPrefix, showOpId], !shown)\n }\n\n render() {\n let { id, method, shown, href } = this.props\n\n return (\n \n
\n {method.toUpperCase()}\n {id}\n
\n \n )\n }\n\n}\n\nOperationLink.propTypes = {\n href: PropTypes.string,\n onClick: PropTypes.func,\n id: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n shown: PropTypes.bool.isRequired,\n showOpId: PropTypes.string.isRequired,\n showOpIdPrefix: PropTypes.string.isRequired\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/core/components/overview.jsx","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { fromJS, List } from \"immutable\"\nimport { getSampleSchema } from \"core/utils\"\n\nconst NOOP = Function.prototype\n\nexport default class ParamBody extends PureComponent {\n\n static propTypes = {\n param: PropTypes.object,\n onChange: PropTypes.func,\n onChangeConsumes: PropTypes.func,\n consumes: PropTypes.object,\n consumesValue: PropTypes.string,\n fn: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n isExecute: PropTypes.bool,\n specSelectors: PropTypes.object.isRequired,\n pathMethod: PropTypes.array.isRequired\n };\n\n static defaultProp = {\n consumes: fromJS([\"application/json\"]),\n param: fromJS({}),\n onChange: NOOP,\n onChangeConsumes: NOOP,\n };\n\n constructor(props, context) {\n super(props, context)\n\n this.state = {\n isEditBox: false,\n value: \"\"\n }\n\n }\n\n componentDidMount() {\n this.updateValues.call(this, this.props)\n }\n\n componentWillReceiveProps(nextProps) {\n this.updateValues.call(this, nextProps)\n }\n\n updateValues = (props) => {\n let { specSelectors, pathMethod, param, isExecute, consumesValue=\"\" } = props\n let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get(\"name\"), param.get(\"in\")) : fromJS({})\n let isXml = /xml/i.test(consumesValue)\n let isJson = /json/i.test(consumesValue)\n let paramValue = isXml ? parameter.get(\"value_xml\") : parameter.get(\"value\")\n\n if ( paramValue !== undefined ) {\n let val = !paramValue && isJson ? \"{}\" : paramValue\n this.setState({ value: val })\n this.onChange(val, {isXml: isXml, isEditBox: isExecute})\n } else {\n if (isXml) {\n this.onChange(this.sample(\"xml\"), {isXml: isXml, isEditBox: isExecute})\n } else {\n this.onChange(this.sample(), {isEditBox: isExecute})\n }\n }\n }\n\n sample = (xml) => {\n let { param, fn:{inferSchema} } = this.props\n let schema = inferSchema(param.toJS())\n\n return getSampleSchema(schema, xml, {\n includeWriteOnly: true\n })\n }\n\n onChange = (value, { isEditBox, isXml }) => {\n this.setState({value, isEditBox})\n this._onChange(value, isXml)\n }\n\n _onChange = (val, isXml) => { (this.props.onChange || NOOP)(this.props.param, val, isXml) }\n\n handleOnChange = e => {\n const {consumesValue} = this.props\n const isJson = /json/i.test(consumesValue)\n const isXml = /xml/i.test(consumesValue)\n const inputValue = isJson ? e.target.value.trim() : e.target.value\n this.onChange(inputValue, {isXml})\n }\n\n toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))\n\n render() {\n let {\n onChangeConsumes,\n param,\n isExecute,\n specSelectors,\n pathMethod,\n\n getComponent,\n } = this.props\n\n const Button = getComponent(\"Button\")\n const TextArea = getComponent(\"TextArea\")\n const HighlightCode = getComponent(\"highlightCode\")\n const ContentType = getComponent(\"contentType\")\n // for domains where specSelectors not passed\n let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get(\"name\"), param.get(\"in\")) : param\n let errors = parameter.get(\"errors\", List())\n let consumesValue = specSelectors.contentTypeValues(pathMethod).get(\"requestContentType\")\n let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes\n\n let { value, isEditBox } = this.state\n\n return (\n
\n {\n isEditBox && isExecute\n ?